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

【C】初阶数据结构3 -- 单链表

  之前在顺序表那一篇文章中,提到顺序表具有的缺点,比如头插,头删时间复杂度为O(n),realloc增容有消耗等。而在链表中,这些问题将得到解决。所以在这一篇文章里,我们将会讲解链表的定义与性质,以及最简单的链表 -- 单链表的结构,以及基础方法的实现。


目录

1  链表

  1) 链表的概念

 2) 结点(节点)

3) 链表结点的结构

4) 链表的性质 

 2  单链表

1) 单链表的头插、尾插

2) 单链表的头删、尾删

3) 单链表在指定位置之前、之后插入数据 


重点一  链表

1  链表

  1) 链表的概念

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

一个链表(这里举例为单链表)的逻辑结构如图所示:

 2) 结点(节点)

  从上面那个链表的结构的图来看,结点就是指上面那个链表中每个独立存在的一个方块,所以一个结点里面包含了两部分内容,一个是这个结点里面存储的数据域,另一个是指向在一个结点的指针域,用来找到下一个结点,然后这里的plist是一个指向链表中第一个结点的指针,里面存储的是第一个结点的地址,可以通过plist来找到后面的结点,从结点的结构来看,知道了plist,也就是链表第一个结点的地址,我们就可以顺着结点中的指针,依次找到每一个结点

3) 链表结点的结构

  上面知道了结点的定义,结点的结构就很容易实现了,就是一个保存结点数据的数据域,还有一个指向下一个结点的指针:

#typedef int ListDataType;
typedef struct ListNode
{ListDataType data;//结点数据struct ListNode* next;//指向下一个结点的指针
}ListNode;

4) 链表的性质 

(1) 由于链表每个结点都是独立的一块空间,结点之间是通过指针连在一起,所以链表在物理结构上不一定是连续的,但是在逻辑结构上是连续的,所以是属于线性表的(2) 每个结点是动态开辟的,而动态开辟的空间都是在内存里一个叫做堆区的空间开辟的(3) 由于每个结点是独立申请的,所以每个结点的物理地址不一定连续

 

重点二  单链表

 2  单链表

  单链表是链表的一种,其结点的结构就是上面那个图那样,每个结点里面就只有一个数据域,还有指向下一个结点的指针域,所以对于单链表来说,只能通过前面的结点找到后面的结点,是不能通过后面的结点找到后面的结点的

  单链表的结构:

typedef int SLTDataType;//singal list node -- 单链表结点typedef struct SListNode
{SLTDataType data;//结点的数据struct SListNode* next;//指向下一个结点的指针
}SLTNode;

  对于一个单链表来说,它的基础方法有链表的头插,头删,尾插,尾删,打印,查找,在指定位置之前插入数据,删除pos(这里的pos是一个节点的地址)结点,在指定位置之后插入数据,删除pos之后的节点, 销毁链表,同样,我们先讲解每一种方法的画图实现,之后再附上代码。


1) 单链表的头插、尾插

  既然是插入结点,那么就肯定会需要新开辟一个结点,而且只要插入结点就需要开辟新结点,所以可以把开辟新结点写成一个函数(buyNode),开辟新结点的功能也很容易实现,只要用malloc函数动态开辟出一个结点大小的空间,然后再让结点的data值赋值为想要开辟新结点的值,然后再让next指针为NULL,就开辟出了一个新结点。

  链表的头插的实现如图

但是我们在实现头插的过程中,发现phead(形参,链表的头节点)会改变指向,会从指向旧链表的头节点改为指向插入的新结点,要想让传过来的实参头插后链表新的首节点,这里必须改变实参(也就是原来链表头节点指针的指向,形参改变影响实参),所以这里必须传指针变量的地址,也就是二级指针

  尾插的实现也类似,如图

在第二步里面,需要通过头节点来找到尾节点,通过头节点找到尾节点的实现思路为:先创建一个节点指针变量 ptail 指向首结点,之后再让循环判断 ptail 的next指针为不为NULL,如果不为NULL,那就让ptail走向下一个结点(注意,这里判断条件不能是ptail为不为NULL,如何是这个判断条件,那么ptail就会走向NULL,后面势必会造成对NULL指针的解引用),代码为:

SLTNode* ptail = phead;
while (ptail->next != NULL)
{ptail = ptail->next;
}

  那么尾插是否需要传二级指针呢?

  其实不管是头插还是尾插,都需要考虑一种特殊情形,那就是链表为空的情况(就是指向头结点的指针为NULL),如果链表为空,上面的两种情况都会造成对空指针的解引用,所以如果链表为空,那就让指向头节点的指针指向新开辟的结点newnode就可以了,但是这里改变的只是形参,所以如果想要让实参也变为指向newnode的指针,就必须传指针的地址,也就是二级指针了。(这里如果看不懂的话,可以看下面的代码理解一下)。

  所以上面找尾节点的代码应该为:

//传过来的二级指针为SLTNode** pphead,指向头节点的指针为*pphead
SLTNode* ptail = *pphead;
while (ptail != NULL)
{ptail = ptail->next;
}

2) 单链表的头删、尾删

  既然是删除结点,那么就需要判断链表为不为空,也就是判断传过来的指向首节点的指针为不为NULL,如果为NULL,就不能删除结点。

  头删的过程如图:

头删也需要注意传递的参数也需要是二级指针(因为形参的改变要影响实参) 。

  尾删的过程如下:

寻找prev和ptail的代码如下:

//传过来的为二级指针**pphead
SLTNode* ptail = *pphead, *prev = NULL;
while (ptail->next != NULL)
{prev = ptail;ptail = ptail->next;
}

   头删和尾删的时候需要注意一种极端情况,那就是只有一个结点的情况((*pphead)->next == NULL),如果只有一个结点,那么prev指针就是NULL指针,那么就会出现NULL指针的解引用,但是对于头删的代码来说,就不会出现这种情况(可以结合下面的代码)。所以尾删只有一个结点的情况需要特殊处理,其实删除只有一个结点的链表尾删和头删都是一样的,所以链表只有一个结点时,尾删也就是头删


3) 单链表在指定位置之前、之后插入数据 

  同样的,既然是插入数据,就需要和头插、尾插一样开辟新的节点(这里与头插、尾插开辟新节点逻辑相同,不再赘述)。这里的位置指的是一个节点,这里叫做pos节点,那么实现逻辑如下图所示:

在pos节点之前插入数据

找到pos节点之前的prev节点的逻辑类似于尾插中找到尾节点,具体逻辑为:先将prev节点指定为首节点,如果prev的next指针不指向pos节点,那就让prev走到它的next指针,写成代码为:

//*pphead为首节点
SLTNode* prev = *pphead;while(prev->next != pos)
{prev = prev->next;
}

   我们来考虑一下特殊情况,那就是如果pos节点就是首节点的话(pos == *pphead),如果继续按照这个逻辑的话,prev节点是首届点,而pos节点也是首节点,那么prev的next指针始终不等于pos,所以最终会造成对NULL指针的解引用,所以如果pos是首节点,这时候需要特殊处理一下,仅需要调用一下头插的函数就可以了(可以结合下面代码思考一下)

在pos节点之后插入数据

   我们再来考虑一下特殊情况,就是如果pos是尾节点的情况,如果再按这个逻辑的话,会发现是没有问题的,不会出现对于NULL指针的解引用情况。但是有一个点需要注意,就是在第二步里面,一定要先让newnode的next指针先改变,再让pos的next指针改变,因为pos的next指针如果先改变的话,那就找不到pos的下一个节点了,且newnode会指向自己的。

  还有就是我们可以看到在pos节点之后插入数据,实现逻辑是没有用到首节点的,所以对于该函数实现的时候,只需要传pos参数和插入的数据 X 两个参数就可以了

  那么在指定位置之前和之后插入数据是否需要判断单链表是否为空呢?实际上是不需要的,因为在pos节点之前、之后插入数据就保证了该单链表是非空的,所以只需要判断pos节点是否为NULL就可以了。


4) 删除指定位置(pos)节点,删除指定位置(pos)之后的节点

  既然是删除节点,那就需要判断链表是否为空(判断逻辑与头删、尾删相同,不再赘述),这两个方法实现逻辑如下:

删除pos节点

  我们来考虑一下特殊情况,那就是如果pos节点是首节点、尾节点时。如果pos是尾节点,根据这个逻辑来实现的话,发现是没有问题的;但是如果pos是首节点,那么根据我们之前找prev节点的逻辑,会出现对NULL指针的解引用的,所以pos是首节点的情况需要特殊处理一下,只需要调用一下头删的代码就可以了。

删除pos节点之后的节点

  该方法的实现时有一个地方需要注意,就是要删除的节点,也就是pos的下一个节点不能为NULL,且该方法的实现只需要传递一个pos参数就可以了。


5) 打印、查找、销毁

  打印函数和查找函数的逻辑比较好实现,只要遍历整个单链表,然后打印每个节点的值或者比较节点的值要和查找的值相不相等就可以了,如果查找到了就返回对应节点的地址,如果没有查找到,就返回NULL。

  销毁链表也很简单,只要从首节点开始遍历,然后销毁每一个节点就可以了,只不过需要注意在释放当前节点之前,需要先把下一个节点的地址存起来,避免找不到下一个节点了。


6) 代码实现 

SList.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLTDataType;//singal list node -- 单链表结点typedef struct SListNode
{SLTDataType data;//结点的数据struct SListNode* next;//指向下一个结点的指针
}SLTNode;//打印
void SLTPrint(SLTNode* phead);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//头删
void SLTPopFront(SLTNode** pphead);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//尾删
void SLTPopBack(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插入数据
void SLTInsert(SLTDataType** pphead, SLTNode* pos, SLTDataType x);
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SLTDestroy(SLTNode** pphead);

SList.c文件:

#include"SList.h"//开辟结点的函数
SLTNode* buyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//开辟失败if (newnode == NULL){perror("malloc fail!\n");//打印错误信息exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}//打印
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);//传过来的不能是空指针if ((*pphead) == NULL){*pphead = buyNode(x);}else{SLTNode* newnode = buyNode(x);//让新开辟的结点next指针指向链表第一个结点newnode->next = *pphead;//再让指向第一个结点的指针指向新开辟的结点*pphead = newnode;}
}//头删
void SLTPopFront(SLTNode** pphead)
{//传过来的指针不能为空,并且链表不能为空assert(pphead && *pphead);//得先让首结点指向首结点的下一个结点,要不然会成野指针SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);//如果是空链表的话,就让指针指向新的结点if (*pphead == NULL){*pphead = buyNode(x);}//链表不为空,得先找到尾节点else{SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}//ptail为尾结点SLTNode* newnode = buyNode(x);ptail->next = newnode;}
}
//尾删
void SLTPopBack(SLTNode** pphead)
{//传过来的地址不能为NULL并且链表为空assert(pphead && *pphead);//如果链表中只有一个结点,那就是头删除if ((*pphead)->next == NULL){SLTPopFront(pphead);}else{//得先找到尾节点和尾节点的前一个结点SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}
}//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{assert(phead);SLTNode* pcur = phead;while (pcur){if (pcur->data == x)return pcur;pcur = pcur->next;}return NULL;
}//在指定位置之前插入数据
void SLTInsert(SLTDataType** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//如果pos节点就是首节点if (pos == *pphead){//头插SLTPushFront(pphead, x);}//如果pos不是首节点else{SLTNode* prev = *pphead;SLTNode* newnode = buyNode(x);while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}
}//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);assert(pos);//如果pos节点是首节点if (pos == *pphead){SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}}//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = buyNode(x);//一定得先让newnode->next = pos->next//再让pos->next = newnodenewnode->next = pos->next;pos->next = newnode;
}//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{//pos的下一个结点不能为空,否则不能删除assert(pos && pos->next);SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}//销毁链表
void SLTDestroy(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* pcur = *pphead;//先存下一个结点SLTNode* next = pcur->next;while (pcur){next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

test.c:

#include"SList.h"void Test()
{SLTNode* plist = NULL;测试头插/*SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);*///测试头删//SLTPopFront(&plist);/*SLTPopFront(&plist);SLTPopFront(&plist);SLTPopFront(&plist);*///SLTPopFront(&plist);//测试尾插SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);//测试尾删/*SLTPopBack(&plist);SLTPopBack(&plist);SLTPopBack(&plist);SLTPopBack(&plist);*///SLTPopBack(&plist);//测试查找/* SLTNode* pfind = SLTFind(plist, 10);if (pfind == NULL){printf("没找到\n");}else{printf("找到了\n");}*/SLTNode* pfind = SLTFind(plist, 1);if (pfind == NULL){printf("没找到\n");}else{printf("找到了\n");}//测试在指定位置之前插入数据//可以测试在1,2,4节点之前插入数据//SLTInsert(&plist, pfind, 5);//测试在指定节点之后插入数据//可以测试在1,2,4节点之后插入数据//SLTInsertAfter(pfind, 5);//测试删除pos节点//可以测试删除1,2,4节点//SLTErase(&plist, pfind);//测试删除pos之后的节点//可以测试删除1,2,3之后的节点//SLTEraseAfter(pfind);//可以使用调试测试销毁是否成功//如果销毁成功,下面打印结果就是NULLSLTDestroy(&plist);//打印SLTPrint(plist);
}int main()
{Test();return 0;
}

  单链表是链表里面结构最简单的一种链表,链表共分为8类(在双向链表里面会讲解),但是由于结构最简单,所以遍历只能从头开始遍历且在方法实现中涉及的细节点很多。同时,单链表中为什么传二级指针也需要自己理解。总之,如果刚开始学习单链表,并不会很容易理解;但是,一旦理解了,相信会对指针的理解更加深刻。

相关文章:

【C】初阶数据结构3 -- 单链表

之前在顺序表那一篇文章中&#xff0c;提到顺序表具有的缺点&#xff0c;比如头插&#xff0c;头删时间复杂度为O(n)&#xff0c;realloc增容有消耗等。而在链表中&#xff0c;这些问题将得到解决。所以在这一篇文章里&#xff0c;我们将会讲解链表的定义与性质&#xff0c;以及…...

STM32 FreeRTOS 基础知识

多任务处理 内核是操作系统的核心组件。诸如 Linux 这样的操作系统采用的内核&#xff0c; 看似允许用户同时访问计算机。很明显&#xff0c;多个用户可以同时执行多个程序。 每个执行程序都是受操作系统控制的任务&#xff08;或线程&#xff09;。如果一个操作系统能够以这…...

初学stm32 --- II2C_AT24C02,向EEPROM中读写数据

目录 IIC总线协议介绍 IIC总线结构图 IIC协议时序 1. ACK&#xff08;Acknowledge&#xff09; 2. NACK&#xff08;Not Acknowledge&#xff09; IO口模拟II2C协议 发送起始信号&#xff1a; 发送停止信号&#xff1a; 检测应答信号&#xff1a; 发送应答信号&#x…...

探索图像编辑的无限可能——Adobe Photoshop全解析

文章目录 前言一、PS的历史二、PS的应用场景三、PS的功能及工具用法四、图层的概念五、调整与滤镜六、创建蒙版七、绘制形状与路径八、实战练习结语 前言 在当今数字化的世界里&#xff0c;视觉内容无处不在&#xff0c;而创建和编辑这些内容的能力已经成为许多行业的核心技能…...

当comfyui-reactor-node 安装失败urllib.error.HTTPError: HTTP Error 403: Forbidden解决方法

comfyUI 节点comfyui-reactor-node 安装 python install 时 报错 urllib.error.HTTPError: HTTP Error 403: Forbidden 如下&#xff1a; (xxx) xxxxxxx:~/sdb/Q/ComfyUI/custom_nodes/comfyui-reactor-node$ python install.py Traceback (most recent call last): File …...

01基本介绍篇(D2_多线程问题)

目录 一、线程的上下文切换问题 1. 基本介绍 2. 多线程一定比单线程快&#xff1f; 3. 如何减少上下文切换 二、线程安全问题 1. 什么是线程安全&#xff1f; 2. java语言中的线程安全 2.1. 不可变 2.2. 绝对线程安全 2.3. 相对线程安全 2.4. 线程兼容 2.5. 线程对立…...

如何保证光谱相机的稳定性和可靠性

光学系统设计与制造 高质量光学元件&#xff1a;采用高精度研磨和镀膜的透镜、棱镜、光栅等光学元件。优质的透镜可以减少像差和色差&#xff0c;确保光线准确聚焦&#xff1b;高质量的镀膜能够提高光学元件的透光率&#xff0c;降低反射损失&#xff0c;并且增强对不同波段光…...

基于springboot+vue的洪涝灾害应急信息管理系统设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…...

登录、注册、忘记密码、首页HTML模板

<!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>登录</title><style>body {display: fl…...

HTML文章翻页功能

效果展示&#xff1a; 效果原理&#xff1a; 1、引入CDN 2、绘制文章翻页样式&#xff0c;以及自动分段 3、获取窗口宽高&#xff0c;计算出当前文章总分段&#xff0c;并实现分页 4、完整代码 <!DOCTYPE html> <html><head><meta charset"utf-8&qu…...

JAVA安全编码规范

1. 数据校验 对外部输入进行校验入参的合法性&#xff0c; 防止内存越界&#xff0c;命令注入&#xff0c;SQL注入&#xff0c;格式化字符串漏洞 校验长度&#xff0c;范围&#xff0c;输入校验采用白名单形式 校验前做归一化处理&#xff0c;使用java.text.Normalizer的n…...

OpenGL —— 基于Qt的视频播放器 - ffmpeg硬解码,QOpenGL渲染yuv420p或nv12视频(附源码)

运行效果 工程说明 源码 vertex.glsl...

爬虫逆向学习(十五):Akamai 3.0反爬分析与sensor-data算法逆向经验

此分享只用于学习用途&#xff0c;不作商业用途&#xff0c;若有冒犯&#xff0c;请联系处理 Akamai 3.0反爬分析与sensor-data算法逆向经验 Akamai开始正题前须知站点信息接口分析反爬点反爬点定位_abck定位结果 逆向前准备工作sensor_data生成位置本地替换文件 请求体sensor…...

java项目启动时,执行某方法

1. J2EE项目 在Servlet类中重写init()方法&#xff0c;这个方法会在Servlet实例化时调用&#xff0c;即项目启动时调用。 import javax.servlet.ServletException; import javax.servlet.http.HttpServlet;public class MyServlet extends HttpServlet {Overridepublic void …...

学会使用开源软件jclasslib 字节码文件的组成 详解

应用场景 1 应用场景 2 学习路线 以正确的姿势打开文件 字节码文件的组成 玩转字节码常用工具 以正确的姿势打开文件 开源软件 jclasslib github 地址 https://github.com/ingokegel/jclasslib 工具使用 字节码文件的组成 基本信息 常量池 字段 方法 属性 详解 魔数 主副版…...

Flask表单处理与验证

Flask是一个轻量级的Python框架&#xff0c;它通过扩展库提供了对表单处理与验证的支持。WTForms是一个流行的Flask扩展库&#xff0c;用于创建和验证Web表单。它提供了一种声明式的方法来定义表单结构和验证逻辑&#xff0c;使得表单处理更为简洁和优雅。下面&#xff0c;我们…...

如何通俗易懂的理解 html js css

HTML、CSS 和 JavaScript 是构建网页的三大核心技术。为了通俗易懂地理解它们&#xff0c;我们可以用一个简单的比喻&#xff1a;**盖房子**。 --- ### 1. **HTML&#xff1a;房子的结构** HTML&#xff08;HyperText Markup Language&#xff09;就像房子的**骨架**。它定义…...

信凯科技业绩波动明显:毛利率远弱行业,资产负债率偏高

《港湾商业观察》施子夫 1月8日&#xff0c;深交所官网显示&#xff0c;浙江信凯科技集团股份有限公司&#xff08;以下简称“信凯科技”&#xff09;主板IPO提交注册。 自2022年递交上市申请&#xff0c;信凯科技的IPO之路已走过两年光景&#xff0c;尽管提交注册&#xff0…...

蓝牙BT04-A的使用与相关AT指令

一、AT指令没有返回的问题及解决方案 检查指令格式&#xff1a; 确认指令格式是否正确&#xff0c;包括特定的命令和结尾的回车换行符&#xff08;n&#xff09;。 检查TX/RX连接&#xff1a; 确认TX&#xff08;发送&#xff09;和RX&#xff08;接收&#xff09;线是否连接正…...

新手如何练习SQL?|掌握

对于新手想要练习SQL语句&#xff0c;可以从以下几个方面入手&#xff1a; 1. 建立理论基础 首先深入理解数据库的核心组件&#xff0c;包括数据库本身、其内部的各个表、表中的字段及其对应的数据类型&#xff08;如字符串、整型、日期等&#xff09;&#xff0c;以及数据库…...

JavaScript宝典下

小哆啦闭关修炼已久&#xff0c;潜心攻读专业秘技&#xff0c;方才下山考研本欲大展宏图&#xff0c;怎奈山河虽壮志难酬&#xff0c;终是觉察考研无望。思来想去&#xff0c;不若弃考研之念&#xff0c;重拾敲代码之道&#xff0c;复盘前端奇术&#xff0c;以备闯荡职场江湖。…...

浅谈云计算12 | KVM虚拟化技术

KVM虚拟化技术 一、KVM虚拟化技术基础1.1 KVM虚拟化技术简介1.2 KVM虚拟化技术架构1.2.1 KVM内核模块1.2.2 用户空间工具&#xff08;QEMU、Libvirt等&#xff09; 二、KVM虚拟化技术原理2.1 硬件辅助虚拟化2.2 VMCS结构与工作机制 三、KVM虚拟化技术面临的挑战与应对策略3.1 性…...

Spring Boot 动态表操作服务实现

Spring Boot 动态表操作服务实现 Spring Boot 动态表操作服务实现1. 环境配置2. JdbcTemplate 的使用2.1 创建动态表2.2 动态添加字段2.3 动态删除字段2.4 动态修改字段类型2.5 删除表的方法实现 3. 小结3.1 可能的优化 Spring Boot 动态表操作服务实现 在现代的应用开发中&am…...

62_Redis服务器集群优化

Redis集群虽然具备高可用特性,且能实现自动故障恢复,但是如果使用不当,也会存在一些问题,总结如下。 集群完整性问题集群带宽问题数据倾斜问题客户端性能问题命令的集群兼容性问题Lua和事务问题1.集群完整性问题 在 Redis 集群的默认配置下,当节点检测到存在至少一个哈希…...

晨辉面试抽签和评分管理系统之九:随机编排考生的分组(以教师资格考试面试为例)

晨辉面试抽签和评分管理系统&#xff08;下载地址:www.chenhuisoft.cn&#xff09;是公务员招录面试、教师资格考试面试、企业招录面试等各类面试通用的考生编排、考生入场抽签、候考室倒计时管理、面试考官抽签、面试评分记录和成绩核算的面试全流程信息化管理软件。提供了考生…...

Linux Top 命令 load average 指标解读

前言 作为平台开发的同学&#xff0c;维护平台稳定性是我们最基本的工作职责&#xff0c;下面主要介绍下top 命令里 &#xff0c;load average 这个指标如何去衡量机器负载程度。 概念介绍 load average 是系统在过去 1 分钟、5 分钟、15 分钟 的平均负载&#xff0c;它表示运…...

Nacos: 一个动态服务发现与配置管理平台

Nacos: 一个动态服务发现与配置管理平台 引言 在微服务架构日益普及的今天&#xff0c;服务之间的调用和配置管理变得越来越复杂。为了简化这一过程并提高开发效率&#xff0c;阿里巴巴推出了Nacos——一个易于使用的动态服务发现、配置管理和服务管理平台。 Nacos是什么&am…...

SpringBoot + 事务钩子函数

一、案例背景 拿支付系统相关的业务来举例。在支付系统中&#xff0c;我们需要记录每个账户的资金流水&#xff08;记录用户A因为哪个操作扣了钱&#xff0c;因为哪个操作加了钱&#xff09;&#xff0c;这样我们才能对每个账户的账做到心中有数&#xff0c;对于支付系统而言&…...

OpenCV相机标定与3D重建(56)估计物体姿态(即旋转和平移)的函数solvePnPRansac()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 使用RANSAC方案从3D-2D点对应关系中找到物体的姿态。 cv::solvePnPRansac 是 OpenCV 中用于估计物体姿态&#xff08;即旋转和平移&#xff09;的…...

【JVM中的三色标记法是什么?】

JVM中的三色标记法是什么? 一、基本概念二、标记过程三、优势与问题四、漏标与多标的解决方案三色标记法(Tri-color Marking Algorithm)是Java虚拟机(JVM)中一种用于追踪对象存活状态的垃圾回收算法。 它基于William D. Hana和Mark S. McCulleghan在1976年提出的两色标记法…...

从0开始学习搭网站第二天

前言&#xff1a;今天比较惭愧&#xff0c;中午打铲吃了一把&#xff0c;看着也到钻二了&#xff0c;干脆顺手把这个赛季的大师上了&#xff0c;于是乎一直到网上才开始工作&#xff0c;同样&#xff0c;今天的学习内容大多来自mdn社区mdn 目录 怎么把文件上传到web服务器采用S…...

43.Textbox的数据绑定 C#例子 WPF例子

固定最简步骤&#xff0c;包括 XAML&#xff1a; 题头里引入命名空间 标题下面引入类 box和block绑定属性 C#&#xff1a; 通知的类&#xff0c;及对应固定的任务 引入字段 引入属性 属性双触发&#xff0c;其中一个更新block的属性 block>指向box的属性 从Textbo…...

钉钉实现第三方登录示例(重复回调问题解析)

钉钉作为专门为企业打造的沟通协助平台&#xff0c;包含的功能很多&#xff0c;考勤打卡&#xff0c;审批&#xff0c;日记&#xff0c;钉盘&#xff0c;钉邮等。基本满足了一些中小企业的大部分工作需求。因此对接钉钉的一些功能模块业务需求在开发中也是比较常见的。钉钉的开…...

Vue2+OpenLayers添加/删除点、点击事件功能实现(提供Gitee源码)

目录 一、案例截图 二、安装OpenLayers库 三、安装Element-UI 四、代码实现 4.1、添加一个点 4.2、删除所有点 4.3、根据经纬度删除点 4.4、给点添加点击事件 4.5、完整代码 五、Gitee源码 一、案例截图 可以新增/删除标记点&#xff0c;点击标记点可以获取到当前标…...

算法妙妙屋-------2..回溯的奇妙律动

回溯算法是一种用于系统性地搜索和解决问题的算法&#xff0c;它以深度优先搜索&#xff08;DFS&#xff09;为基础&#xff0c;用来探索所有可能的解决方案。通过递归地尝试候选解并在必要时回退&#xff08;即“回溯”&#xff09;&#xff0c;它能够高效地解决许多涉及组合、…...

pytest-instafail:让测试失败信息即时反馈

pytest-instafail&#xff1a;让测试失败信息即时反馈 前言一、简介二、优势三、安装与使用3.1 未安装时运行情况3.2 安装3.3 已安装时运行情况3.3 pytest.ini 配置选项 四、对比 总结 前言 当测试用例数量庞大时&#xff0c;定位测试失败的原因往往耗时费力。此时&#xff0c;…...

K8S--配置存活、就绪和启动探针

目录 1 本人基础环境2 目的3 存活、就绪和启动探针介绍3.1 存活探针3.2 就绪探针3.3 启动探针 4 探针使用场景4.1 存活探针4.2 就绪探针4.3 启动探针 5 配置存活、就绪和启动探针5.1 定义存活探针5.2 定义一个存活态 HTTP 请求接口5.3 定义 TCP 的就绪探针、存活探测5.4 定义 g…...

solidity基础 -- 枚举

在智能合约开发领域&#xff0c;Solidity语言因其简洁高效而被广泛使用。其中&#xff0c;枚举&#xff08;enum&#xff09;作为一种特殊的数据类型&#xff0c;为合约的状态管理提供了极大的便利。本文将通过一个具体的Solidity合约示例&#xff0c;深入探讨枚举的定义、使用…...

重回C语言之老兵重装上阵(六)枚举

1. 什么是枚举 (enum)&#xff1f; 枚举&#xff08;enum&#xff09;是 C 语言中的一种数据类型&#xff0c;用于定义一组具名的整数常量。它可以使代码更加可读&#xff0c;帮助程序员更容易理解程序中的常量值。通过枚举&#xff0c;程序员可以使用有意义的名称来代替数字&…...

python+playwright自动化测试(一):安装及简单使用,截图录屏

目录 基本使用 浏览器调用 启用浏览器 创建窗口对象 访问URL 页面的刷新、返回、前进 关闭 截图、录屏、保存pdf 截图 录屏 保存为pdf 设置窗口大小 调试模式 手机模式及new_context的更多参数 手机模式 new_context的其他参数 设置语言和时区 设置和修改位置…...

Mysql--架构篇--体系结构(连接层,SQL层,存储引擎层,文件存储层)

MySQL是一种广泛使用的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;其体系结构设计旨在提供高效的数据存储、查询处理和事务管理。MySQL的体系结构可以分为多个层次&#xff0c;每个层次负责不同的功能模块。 MySQL的体系结构主要由以下几个部分组成&#…...

git merge 压缩提交

在 Git 中&#xff0c;执行 git merge 时可以通过一些操作来“压缩”提交&#xff0c;通常是指将合并过程中的多个提交压缩成一个单一的提交。这可以通过使用 --squash 选项来完成&#xff0c;或者在合并后进行交互式 rebase。以下是两种常见的方法&#xff1a; 方法 1&#x…...

Python脚本自动发送电子邮件

要编写一个Python脚本来自动发送电子邮件&#xff0c;你可以使用smtplib库来处理SMTP协议&#xff0c;以及email库来构建邮件内容。 安装必要的库 通常情况下&#xff0c;smtplib和email库是Python标准库的一部分&#xff0c;因此不需要额外安装。如果你使用的是较旧的Python版…...

uniapp中rpx和upx的区别

在 UniApp 中&#xff0c;rpx 和 upx 是两种不同的单位&#xff0c;它们的主要区别在于适用的场景和计算方式。 ### rpx&#xff08;Responsive Pixel&#xff09; - **适用场景**&#xff1a;rpx 是一种响应式单位&#xff0c;主要用于小程序和移动端的布局。 - **计算方式**…...

CentOS 9 Stream 中查看 Python 版本并升级 Python

CentOS 9 Stream 中查看 Python 版本并升级 Python 1. 查看当前 Python 版本2. 升级 Python 版本&#xff08;1&#xff09;安装开发工具&#xff08;2&#xff09;安装必要的依赖包&#xff08;3&#xff09;下载和安装新版本的 Python&#xff08;4&#xff09;验证安装 3. …...

可以用于分割字符串的方法(python)

一、str.split(sep,maxsplit)函数&#xff08;返回列表&#xff09; sep&#xff1a;分隔符 maxsplit&#xff1a;分割次数 a"Hello world" list1a.split(" ",1) print(list1) 结果&#xff1a; [Hello, world] 二、str.rsplit(sep,maxsplit)函数&…...

【Vue】全局/局部组件使用流程(Vue2为例)

全局组件和局部组件区别 如何使用 全局组件&#xff1a;全局注册后&#xff0c;可以在任意页面中直接使用。局部组件&#xff1a;在页面中需要先导入子组件路径&#xff0c;注册组件才能使用。 适用场景 全局组件&#xff1a;适用于高频使用的组件&#xff0c;如导航栏、业…...

virtual box虚拟机误删Python3.6后导致UBUNTU18.04开机无UI界面(进不了desktop)的解决方法

最近在解决一个python引起的问题的时候&#xff0c;作者心一狠&#xff0c;删了系统自带的python3.6&#xff0c; 顺便还删了python3。导致重启后ubuntu的virtual box虚拟机无法看到UI登录界面&#xff0c;只给我了孤零零的命令行。装了很多东西不可能重装&#xff0c;无奈只能…...

虚拟线程JDK与Spring Core Reactor

两种虚拟线程对比&#xff1a;JDK vs. Spring Core Reactor性能对比 1、基于 JDK 的虚拟线程实现&#xff1a; 摘自实际代码&#xff1a; public static void withFlatMapUsingJDK() { ... var virtualThreadExecutor Executors.newThreadPerTaskExecutor( Thread .ofVirtual…...

纯 Python、Django、FastAPI、Flask、Pyramid、Jupyter、dbt 解析和差异分析

一、纯 Python 1.1 基础概念 Python 是一种高级、通用、解释型的编程语言&#xff0c;以其简洁易读的语法和丰富的标准库而闻名。“纯 Python” 在这里指的是不依赖特定的 Web 框架或数据分析工具&#xff0c;仅使用 Python 原生的功能和标准库来开发应用程序或执行任务。 1.…...