当前位置: 首页 > article >正文

数据结构初阶:单链表

序言:

本篇博客主要介绍单链表的基本概念,包括如何定义和初始化单链表,以及如何进行数据的插入,删除和销毁等操作。

1.单链表

1.1 概念与结构

概念:链表是一种非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。物理存储结构上非连续,

 举例来说,淡季时车次的车厢会相应减少,旺季时车次的车厢会额外增加⼏节。只需要将火车里的某节车厢去掉/ 加上,不会影响其他车厢,每节车厢都是独立存在的。

类比于链表,每节“车厢”是什么样的呢?

1.1.1 结点

与顺序表不同的是,链表里的每节“车厢”都是独立申请下来的空间,我们称之为“结点”

结点的组成主要是有两个部分:当前结点要保存的数据和保存下一个结点的地址(指针变量)。

图中指针变量plist保存的是第⼀个结点的地址,我们称plist此时“指向”第⼀个结点,如果我们希望 plist“指向”第二个结点时,只需要修改plist保存的内容为0x0012FFA0。

 链表中每个结点都是独⽴申请的(即需要插⼊数据时才去申请⼀块结点的空间),我们需要通过指针 变量来保存下⼀个结点位置才能从当前结点找到下⼀个结点。

1.1.2 链表的性质

1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续

2、结点⼀般是从堆上申请的

3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

结合学到的结构体知识,我们可以给出每个结点对应的结构体代码:

假设当前保存的结点为整型:

struct SListNode
{int data; //结点数据struct SListNode* next; //指针变量⽤保存下⼀个结点的地址
};

当我们想要保存一个整型数据时,实际是向操作系统申请了一块内存,这个内存不仅要保存整型数据,也要保存下一个结点的地址(当下一个结点为空时保存的地址为空)。

当我们想要从第一个结点走到最后一个结点时,只需要在当前结点拿上下一个结点的地址就可以了。

1.1.3 链表的打印

给定的链表结构,如何实现结点从头到尾的打印?

1.2 实现单链表
1.2.1 头文件的包含
#pragma once
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>//定义链表的结构
//定义节点的结构
typedef int SLTDataType;
typedef struct SListNode {SLTDataType data;struct SListNode* next;//指向下一个节点的指针
}SLTNode;
//typedef struct SListNode SLTNode;//phead:头(首)节点
void SLTPrint(SLTNode* phead);//尾插
void SLTPushBack(SLTNode** phead, SLTDataType x);//头插
void SLTPushFront(SLTNode** phead, SLTDataType x);//尾删
void SLTPopBack(SLTNode** pphead);//头删
void SLTPopFront(SLTNode** phead);//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos);//销毁链表
void SListDestroy(SLTNode** pphead);
1.2.2 打印单链表
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur != NULL){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}

 打印单链表逻辑:

这段 C 语言代码定义了一个名为`SLTPrint`的函数,用于打印一个链表的内容。

函数的参数是一个指向链表头节点的指针`phead`。在函数内部,定义了一个指针`pcur`并初始化为`phead`,然后通过一个循环遍历链表。在循环中,使用`printf`函数输出当前节点的数据,并将指针`pcur`指向下一个节点,直到`pcur`为`NULL`,表示链表遍历结束。最后,输出`NULL`表示链表结束的标志。

1.2.3 向操作系统申请一个新结点
//向操作系统申请一个新节点
SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}

 逻辑概述:

这段 C 语言代码定义了一个名为 SLTBuyNode 的函数,用于向操作系统申请一个新的链表节点。

函数的参数是一个 SLTDataType 类型的变量 x,用于指定新节点的数据。在函数内部,使用 malloc 函数分配一个 SLTNode 类型大小的内存空间,并将其地址赋给指针 newnode。然后,通过判断 newnode 是否为 NULL 来检查内存分配是否成功。如果分配失败,使用 perror 函数输出错误信息,并使用 exit 函数终止程序运行。如果分配成功,将参数 x 赋值给新节点的 data 成员,并将新节点的 next 成员初始化为 NULL。最后,函数返回新节点的指针。

 1.2.4 尾插
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = SLTBuyNode(x);//链表为空,phead直接指向newnode节点if (*pphead == NULL){*pphead = newnode;}else {//链表不为空,找尾节点,将尾节点和新节点连接起来SLTNode* ptail = *pphead;while (ptail->next)//等价于ptail->next != NULL{ptail = ptail->next;}//ptail newnodeptail->next = newnode;}
}

逻辑概述:

这段 C 语言代码定义了一个名为 SLTPushBack 的函数,用于在链表的尾部插入一个新节点。

函数的参数是一个指向链表头节点指针的指针 pphead 和一个 SLTDataType 类型的变量 x。在函数内部,首先调用 SLTBuyNode 函数创建一个新节点 newnode ,并将 x 作为其数据。

然后,通过判断 *pphead 是否为 NULL 来确定链表是否为空。如果链表为空,将 *pphead 直接指向 newnode 。

如果链表不为空,则通过一个循环找到链表的尾节点 ptail 。在循环中,只要 ptail->next 不为 NULL ,就将 ptail 指向下一个节点。当循环结束时,ptail 就指向了尾节点。最后,将 ptail 的 next 指针指向新节点 newnode ,完成尾部插入操作。

1.2.5 头插
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);//newnode *ppheadnewnode->next = *pphead;*pphead = newnode;
}

 逻辑概述:

这段 C 语言代码定义了一个名为SLTPushFront的函数,用于在链表的头部插入一个新节点。

函数的参数是一个指向链表头节点指针的指针pphead和一个SLTDataType类型的变量x在函数内部,首先使用assert函数检查pphead是否有效。然后,调用SLTBuyNode函数创建一个新节点newnode,并将x作为其数据。

接下来,将新节点的next指针指向当前的头节点(即*pphead),然后将*pphead更新为新节点newnode,从而实现了在链表头部插入新节点的操作。

1.2.6  尾删
//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//只有一个结点的时候if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* prev = NULL;SLTNode* ptail = &pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}//prev tailprev->next = NULL;free(ptail);ptail = NULL;}
}

 逻辑概述:

这段 C 语言代码定义了一个名为 SLTPopBack 的函数,用于删除链表的尾节点。

函数的参数是一个指向链表头节点指针的指针 pphead 。在函数内部,首先使用 assert 函数检查 pphead 是否有效以及链表是否不为空。

如果链表只有一个节点,那么直接释放该节点的内存,并将 *pphead 置为 NULL 。

如果链表有多个节点,那么通过一个循环找到尾节点 ptail 和其前一个节点 prev 。在循环中,只要 ptail->next 不为 NULL ,就将 prev 更新为 ptail ,并将 ptail 指向下一个节点。当循环结束时, ptail 就指向了尾节点, prev 指向尾节点的前一个节点。然后,将 prev->next 置为 NULL ,断开与尾节点的连接,释放尾节点的内存,并将 ptail 置为 NULL 。

 1.2.7 头删
//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}

 逻辑概述:

这段 C 语言代码定义了一个名为SLTPopFront的函数,用于删除链表的头节点。

函数的参数是一个指向链表头节点指针的指针pphead。在函数内部,首先使用assert函数检查pphead是否有效以及链表是否不为空。

然后,通过(*pphead)->next获取头节点的下一个节点,并将其赋给next指针。接着,释放头节点的内存空间,最后将*pphead指向next,完成头节点的删除操作。

 1.2.8  查找
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}//未找到return NULL;
}

 逻辑概述:

这段 C 语言代码定义了一个名为 SLTFind 的函数,用于在链表中查找指定值的节点。

函数的参数是一个指向链表头节点的指针 phead 和一个 SLTDataType 类型的变量 x ,表示要查找的值。在函数内部,定义一个指针 pcur 并初始化为 phead ,然后通过一个循环遍历链表。在循环中,检查当前节点的 data 值是否等于要查找的值 x 。如果相等,就返回当前节点的指针;如果不相等,就将 pcur 指向下一个节点,继续循环。如果循环结束后都没有找到匹配的节点,就返回 NULL ,表示未找到。

 1.2.9 在指定位置之前插入数据
//在指定位置之前插入数据
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && pos);//pos就是头结点if(pos == *pphead){//头插SLTPushFront(pphead, x);}else {SLTNode* newnode = SLTBuyNode(x);//pos在头结点之后--->找pos前驱节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev newnode posnewnode->next = pos;prev->next = newnode;}
}

 逻辑概述:

这段 C 语言代码定义了一个名为SLInsert的函数,用于在链表的指定位置之前插入一个新节点。

函数的参数是一个指向链表头节点指针的指针pphead、一个指向要插入位置的节点指针pos以及一个SLTDataType类型的变量x,表示要插入的数据。

在函数内部,首先使用assert函数检查ppheadpos是否有效。

如果pos就是头节点,那么就调用SLTPushFront函数进行头插操作。

如果pos在头节点之后,那么就调用SLTBuyNode函数创建一个新节点newnode,并通过一个循环找到pos的前驱节点prev。在循环中,只要prev->next不等于pos,就将prev指向下一个节点。当循环结束时,prev就指向了pos的前驱节点。然后,将新节点的next指针指向pos,将prevnext指针指向新节点,完成在指定位置之前插入新节点的操作。

 1.2.10 在指定位置之后插入数据
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);//pos newnode pos->nextnewnode->next = pos->next;pos->next = newnode;
}

 逻辑概述:

这段 C 语言代码定义了一个名为 SLTInsertAfter 的函数,用于在链表的指定位置之后插入一个新节点。

函数的参数是一个指向指定位置的节点指针 pos 和一个 SLTDataType 类型的变量 x,表示要插入的数据。

在函数内部,首先使用 assert 函数检查 pos 是否有效。然后,调用 SLTBuyNode 函数创建一个新节点 newnode,并将 x 作为其数据。

接下来,将新节点的 next 指针指向 pos 的下一个节点,然后将 pos 的 next 指针指向新节点,完成在指定位置之后插入新节点的操作。

1.2.11 删除pos结点
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos);//要删除的结点刚好就是头结点---头删if (pos == *pphead){SLTPopFront(pphead);}else {//prevSLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev pos pos->nextprev->next = pos->next;free(pos);pos = NULL;}
}

逻辑概述:

这段 C 语言代码定义了一个名为SLTErase的函数,用于删除链表中的指定节点。

函数的参数是一个指向链表头节点指针的指针pphead和一个指向要删除节点的指针pos

在函数内部,首先使用assert函数检查ppheadpos是否有效。

如果要删除的节点恰好是头节点,那么就调用SLTPopFront函数进行头删操作。

否则,通过一个循环找到要删除节点的前驱节点prev。在循环中,只要prev->next不等于pos,就将prev指向下一个节点。当循环结束时,prev就指向了要删除节点的前驱节点。然后,将prev->next指向pos的下一个节点,释放pos所占用的内存空间,并将pos置为NULL,完成删除节点的操作。

 1.2.11 删除pos结点之后的数据
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);//pos del del->nextSLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}

逻辑概述:

这段 C 语言代码定义了一个名为 SLTEraseAfter 的函数,用于删除链表中指定节点 pos 之后的节点。

函数的参数是一个指向指定节点的指针 pos 。在函数内部,首先使用 assert 函数检查 pos 以及 pos->next 是否有效。

然后,定义一个指针 del 并将其指向 pos 的下一个节点。接着,将 pos 的 next 指针指向 del 的下一个节点。之后,释放 del 所占用的内存空间,并将 del 置为 NULL ,完成删除 pos 之后节点的操作。

 1.2.12 销毁链表
//销毁链表
void SListDestroy(SLTNode** pphead)
{assert(pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}
1.3 源码
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur != NULL){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}//向操作系统申请一个新节点
SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{SLTNode* newnode = SLTBuyNode(x);//链表为空,phead直接指向newnode节点if (*pphead == NULL){*pphead = newnode;}else {//链表不为空,找尾节点,将尾节点和新节点连接起来SLTNode* ptail = *pphead;while (ptail->next)//等价于ptail->next != NULL{ptail = ptail->next;}//ptail newnodeptail->next = newnode;}
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);//newnode *ppheadnewnode->next = *pphead;*pphead = newnode;
}//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//只有一个结点的时候if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* prev = NULL;SLTNode* ptail = &pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}//prev tailprev->next = NULL;free(ptail);ptail = NULL;}
}//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}//未找到return NULL;
}//在指定位置之前插入数据
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead && pos);//pos就是头结点if(pos == *pphead){//头插SLTPushFront(pphead, x);}else {SLTNode* newnode = SLTBuyNode(x);//pos在头结点之后--->找pos前驱节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev newnode posnewnode->next = pos;prev->next = newnode;}
}//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);//pos newnode pos->nextnewnode->next = pos->next;pos->next = newnode;
}//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos);//要删除的结点刚好就是头结点---头删if (pos == *pphead){SLTPopFront(pphead);}else {//prevSLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//prev pos pos->nextprev->next = pos->next;free(pos);pos = NULL;}
}
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{assert(pos && pos->next);//pos del del->nextSLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}
//销毁链表
void SListDestroy(SLTNode** pphead)
{assert(pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

2. 主函数

#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"//手动构造一个链表
//并且打印链表
void test01()
{SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));node1->data = 1;node2->data = 2;node3->data = 3;node4->data = 4;node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;//打印链表SLTNode* plist = node1;SLTPrint(plist);}void test02()
{SLTNode* plist = NULL;//往空链表中插入节点SLTPushBack(&plist, 1);SLTPrint(plist);SLTPushBack(&plist, 2);SLTPrint(plist);SLTPushBack(&plist, 3);SLTPrint(plist);SLTPushBack(&plist, 4);SLTPrint(plist);
}int main()
{//test01();test02();return 0;
}

3. 小结

以上便是本篇博客的所有内容,主要是关于单链表的增,删,查,改的操作,如果这篇博客对诸君有所帮助,还请点点赞。

相关文章:

数据结构初阶:单链表

序言&#xff1a; 本篇博客主要介绍单链表的基本概念&#xff0c;包括如何定义和初始化单链表&#xff0c;以及如何进行数据的插入&#xff0c;删除和销毁等操作。 1.单链表 1.1 概念与结构 概念&#xff1a;链表是一种非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过…...

北斗导航 | 改进伪距残差矢量的接收机自主完好性监测算法原理,公式,应用,RAIM算法研究综述,matlab代码

改进伪距残差矢量的接收机自主完好性监测算法研究 摘要 接收机自主完好性监测(RAIM)是保障全球卫星导航系统(GNSS)可靠性的核心技术。本文针对传统伪距残差矢量法在微小故障检测和多故障隔离中的不足,提出一种融合加权奇偶空间与动态阈值调整的改进算法。通过理论推导验证…...

RabbitMQ高级特性--TTL和死信队列

目录 1.TTL 1.1设置消息的TTL 1.1.1配置交换机&队列 1.1.2发送消息 1.1.3运行程序观察结果 1.2设置队列的TTL 1.2.1配置队列和交换机的绑定关系 1.2.2发送消息 1.2.3运行程序观察结果 1.3两者区别 2.死信队列 2.1 声名队列和交换机 2.2正常队列绑定死信交换机 …...

Java后端开发: 如何安装搭建Java开发环境《安装JDK》和 检测JDK版本

文章目录 一、JDK的安装1、 打开 Oracle 官方网址2、点击产品 二、检测JDK是否安装成功以及JDK版本的查看1. 打开命令行窗口检测是否安装成功查看 JDK 版本 一、JDK的安装 1、 打开 Oracle 官方网址 Oracle官网地址:https://www.oracle.com/cn/ 2、点击产品 打开下载的JDK文件…...

LabVIEW液压控制系统开发要点

液压控制系统开发需兼顾高实时性、强抗干扰性和安全性&#xff0c;尤其在重工业场景中&#xff0c;毫秒级响应延迟或数据异常都可能导致设备损坏。本文以某钢厂液压升降平台项目为例&#xff0c;从硬件选型、控制算法、安全机制三方面&#xff0c;详解LabVIEW开发中的关键问题与…...

鸿蒙Flutter实战:18-组合而非替换,现有插件快速鸿蒙化

引言 在对插件鸿蒙化时&#xff0c;除了往期文章现有Flutter项目支持鸿蒙II中讲到的使用 dependency_overrides 来配置鸿蒙适配库的两种方式以外&#xff0c;如果三方插件本身使用了联合插件的形式&#xff0c;也可以通过下面这种方式来添加鸿蒙平台的实现&#xff1a; depen…...

Qt之Service开发

一、概述 基于Qt的用于开发系统服务(守护进程)和后台服务,有以下几个优秀的开源 QtService 框架和库。 1. QtService (官方解决方案) GitHub: https://github.com/qtproject/qt-solutions/tree/master/qtservice 特点: 官方提供的服务框架 支持 Windows 服务和 Linux 守护…...

MFC添加免费版大漠3.1233

先创建一个MFC工程&#xff0c; 添加dm.dll 方法一&#xff1a;通过类向导-添加类-类型库中的MFC类-文件&#xff0c;选择dm.dll&#xff0c;如果没有"添加类型库中的MFC类"选项就用方法二添加 方法二&#xff1a;添加-新建项-MFC-Active或TypeLib-实现接口位置选…...

vue 图片放大到全局

背景&#xff1a; 在vue项目中&#xff0c;el-image组件图片组件用于展示图片&#xff0c;组件自带的属性preview-teleported&#xff0c;设置为true可以控制图片放大到全局 实现效果&#xff1a; 核心代码&#xff1a; //图片地址&#xff1a;BASEUrl /file/ item.file //这…...

人工智能入门(1)

人工智能导引 文章目录 人工智能导引artifiicial intelligence由图灵测试出发的六个领域贝叶斯方法分析成为大多数AI系统中不确定推理的现代方法基础 研究方法 机器学习计算机利用已经有的数据样本&#xff0c;得出某种规律模型&#xff0c;并利用模型预测未来的一种方法 回归算…...

Python爬虫:Feapder 的详细使用和案例

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 1. Feapder 概述1.1 Feapder介绍1.2 Feapder 核心特点1.3 Feapder 主要组件1.4 Feapder的安装2. 基础爬虫编写2.1 创建爬虫2.2 运行爬虫3. 数据采集案例3.1 新闻网站采集3.2 电商商品采集3.3 使用 Spider 类创建更强大爬…...

mybatis里in关键字拼接id问题

我们一般会把ids集合用StrUtil.join(‘,’)转成"1,2,3"这种形式 然后放入in中 我们会这么写: select id, nick_name, icon from tb_user where id in (#{ids}) order by FIELD(id, #{ids})结果发现sql执行是这样的: select id, nick_name, icon from tb_user where…...

在rockylinux9.4安装mongodb报错:缺少:libcrypto.so.10文件库

问题点&#xff1a; rockylinux9.4系统环境报错&#xff1a; ./mongod: error while loading shared libraries: libcrypto.so.10: cannot open shared object file: No such file or directory 解决方法&#xff1a; Ps&#xff1a;解压之后&#xff0c;检查mongodb的依赖环境…...

Spring Boot集成阿里云OSS:对象存储实战指南

Spring Boot集成阿里云OSS&#xff1a;对象存储实战指南 1. OSS是什么&#xff1f;为什么选择阿里云OSS&#xff1f; 对象存储&#xff08;OSS&#xff09; 是一种用于存储非结构化数据&#xff08;如图片、视频、日志文件&#xff09;的云服务&#xff0c;核心功能包括&#…...

【力扣hot100题】(019)旋转图像

比较考验脑子转不转得过来&#xff0c;最好先在纸上画一下图整理思路&#xff0c;不要和我一样上来就无脑套循环。 理解了思路还是好做的&#xff0c;每个小循环转一圈&#xff0c;大循环代表转的第几圈。小循环循环n-2i-1次&#xff0c;大循环循环&#xff08;n1&#xff09;…...

06_约束

文章目录 一、是什么二、实体完整性约束2.1、主键约束2.2、主键自增长2.3、唯一约束 三、域完整性约束3.1、非空约束3.2、默认值 四、引用完整性约束 一、是什么 用于限制加入表的数据的类型和规范&#xff0c;约束是添加在列上的&#xff0c;用来约束列的。 分类&#xff1a; …...

Anolis OS 8.4修复CVE-1999-0554漏洞记录

1. 使用TCP Wrappers配置白名单 通过修改/etc/hosts.allow和/etc/hosts.deny文件&#xff0c;仅允许特定IP访问NFS的mountd服务&#xff08;需确保系统支持TCP Wrappers&#xff09;&#xff1a; 编辑/etc/hosts.allow&#xff0c;添加允许的客户端IP&#xff08;如192.168.1…...

Seata AT模式的一些常见问题及其源码解析

Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式解决方案。 Seata AT 基于两阶段提交协议的演变&#xff1a; 一阶段&#xff1a;业…...

华为GaussDB数据库的手动备份与还原操作介绍

数据库的备份以A机上的操作为例。 1、使用linux的root用户登录到GaussDB服务器。 2、用以下命令切换到 GaussDB 管理员用户&#xff0c;其中&#xff0c;omm 为当前数据库的linux账号。 su - omm 3、执行gs_dump命令进行数据库备份&#xff1a; 这里使用gs_dump命令进行备…...

2025年3月29日(matlab -ss -lti)

线性时不变系统&#xff08;LTI系统&#xff09;的定义与核心特性 线性时不变系统&#xff08;Linear Time-Invariant System&#xff09;是信号与系统分析中的基础模型&#xff0c;其核心特性包括线性和时不变性。以下从定义、验证方法和应用场景展开说明&#xff1a; 1. 线性…...

网络原理-TCP/IP

网络原理学习笔记&#xff1a;TCP/IP 核心概念 本文是我在学习网络原理时整理的笔记&#xff0c;主要涵盖传输层、网络层和数据链路层的核心协议和概念&#xff0c;特别是 TCP, UDP, IP, 和以太网。 一、传输层 (Transport Layer) 传输层负责提供端到端&#xff08;进程到进…...

服务器磁盘卷组缓存cache设置介绍

工具1&#xff1a; storcli a. 确认软件包是否安装 [rootlocalhost ~]#rpm -qa | grep storcli storcli-1.21.06-1.noarch 备注&#xff1a;若检索结果为空&#xff0c;需要安装对应的软件安装包。安装命令如下&#xff1a; #rpm -ivh storcli-xx-xx-1.noarch.rpm b. 查看逻辑…...

Unity顶点优化:UV Splits与Smoothing Splits消除技巧

一、顶点分裂问题概述 1. 什么是顶点分裂 顶点分裂(Vertex Splits)是3D渲染中常见的性能问题&#xff0c;当模型需要为同一顶点位置存储不同属性值时&#xff0c;会创建多个顶点副本。主要分为两类&#xff1a; UV Splits&#xff1a;由UV不连续引起 Smoothing Splits&#…...

第五十三章 Spring之假如让你来写Boot——环境篇

Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…...

Router [Continuation Settings]

楼上网络CMCC-Wmew&#xff0c;楼下接收不到&#xff0c;可能因为喜好弱&#xff0c;再弄一台路由器中转一下 Router [Continuation Settings] 路由器中续设置 到这里这台K3的路由器设置完成了&#xff0c;作为转发&#xff0c;中续&#xff0c;她还需要设置上游路由器&#…...

Zookeeper中的Zxid是如何设计的

想获取更多高质量的Java技术文章&#xff1f;欢迎访问Java技术小馆官网&#xff0c;持续更新优质内容&#xff0c;助力技术成长 Java技术小馆官网https://www.yuque.com/jtostring Zookeeper中的Zxid是如何设计的 如果你们之前学习过 ZooKeeper&#xff0c;你们可能已经了解…...

蓝桥云客 岛屿个数

0岛屿个数 - 蓝桥云课 问题描述 小蓝得到了一副大小为 MN 的格子地图&#xff0c;可以将其视作一个只包含字符 0&#xff08;代表海水&#xff09;和 1&#xff08;代表陆地&#xff09;的二维数组&#xff0c;地图之外可以视作全部是海水&#xff0c;每个岛屿由在上/下/左/右…...

深度学习篇---paddleocr正则化提取

文章目录 前言一、代码总述&介绍1.1导入必要的库1.1.1cv21.1.2re1.1.3paddleocr 1.2初始化PaddleOCR1.3打开摄像头1.4使用 PaddleOCR 进行识别1.5定义正则表达式模式1.6打印提取结果1.7异常处理 二、正则表达式2.1简介2.2常用正则表达式模式及原理2.2.1. 快递单号模式2.2.2…...

Android 蓝牙/Wi-Fi通信协议之:低功耗蓝牙(BLE 4.0+)介绍

介绍&#xff1a;蓝牙通信协议详解 1. 蓝牙协议分层 Android主要支持**经典蓝牙&#xff08;Bluetooth Classic&#xff09;和低功耗蓝牙&#xff08;BLE&#xff09;**两种模式&#xff1a; 经典蓝牙&#xff08;BT 2.1/3.0&#xff09; 低功耗蓝牙&#xff08;BLE 4.0&…...

流影---开源网络流量分析平台(四)(分析引擎部署)

目录 功能介绍 部署过程 一、安装依赖环境 二、源码编译部署 三、运行环境配置 四、运行配置 功能介绍 本章我将继续安装流影的分析引擎组件首先&#xff0c;ly_analyser是流影的威胁行为分析引擎&#xff0c;读取netflow v9格式的数据作为输入&#xff0c;运行各种威胁行…...