【数据结构】顺序表和链表详解(上)

article/2025/8/4 7:46:31

前言:上期我们介绍了算法的复杂度,知道的算法的重要性同时也了解到了评判一个算法的好与坏就去看他的复杂度(主要看时间复杂度),这一期我们就从顺序表和链表开始讲起。

文章目录

  • 一,顺序表
    • 1,线性表
    • 2,顺序表
    • 3,顺序表的分类
    • 4,动态顺序表的实现
      • 1,
        • 1,头插
        • 2,尾插
        • 2,在指定位置前插入数据
      • 2,
        • 1,头删
        • 2,尾删
        • 3,删除pos位置(指定位置)的数据
      • 3,
      • 4,
      • 5,销毁
  • 二,顺序表的总结和问题的思考
  • 三,链表
    • 1,什么是链表?
    • 2,什么是结点?
    • 3,链表的分类
    • 4,单链表的实现
      • 1,
        • 1,头插
        • 2,尾插
        • 3,在指定位置之前插入数据
        • 4,在指定位置之后插入数据
      • 1,
        • 1,头删
        • 2,尾删
        • 3,删除pos位置之前的结点
        • 4,删除pos位置之后的结点
      • 3,
      • 4 ,链表的销毁

一,顺序表

在介绍顺序表之前我们先引出一个概念:线性表

1,线性表

什么是线性表?

线性表(linear list)是n个具有相同特性的数据元素的有限序列。
线性表是⼀种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…。

· 线性表在逻辑上是线性结构也就是连续的一条直线;但在物理结构上并不一定是连续的,线性表在物理上储存时通常以数组或者链式结构的形式储存。

2,顺序表

什么是顺序表?
概念:顺序表是⽤⼀段物理地址连续的存储单元依次存储数据元素的线性结构,⼀般情况下采⽤数组存储。

通过概念不难发现顺序表它也是一个线性表,那就有线性表所对应的性质。那么线性表既然采用数组存储那么就说明它的底层就是一个数组,我们对于顺序表的操作就是对数组的操作。
在这里插入图片描述
从上图我们可以看地出来,顺序表地逻辑结构是线性的,物理结构也是线性的。
逻辑结构是认为构想出来的的,比如上图就是人为想象出来的。
而物理结构是线性的是因为顺序表底层是数组,数组它本身就是连续存放数据的。

看到这有人就会有疑问了说顺序表和数组到底有什么区别呢?

首先数组是一个可以存放n个相同特性元素的集合,顺序表底层使用的是数组所以顺序也可以存放n个具有相同特性元素的集合,但是顺序表可以对它的底层数组执行一些操作比如对数组内部的数据进行增,删,查,改等操作。

所以可以理解为顺序表是对数组的一个封装,可以直接操作数组。

3,顺序表的分类

顺序表分为动态顺序表和静态顺序表两种。
静态顺序表 即使用定长数组
在这里插入图片描述

我们很容易发现它的缺点就是数组空间的问题,如果我们初始时的空间给大了却只存储很少的数据就造成了空间的浪费,如果我们初始时给的空间太小如果要存储大量的数据有会存在空间不足的问题,所以在实现顺序表时一般不采用静态顺序表的方式。

再来看下一种
动态顺序表 即空间大小可以随时变化
在这里插入图片描述

将数组改成指针的形式之后就可以通过malloc,realloc和calloc函数来扩展空间了,就很好的解决了静态顺序表对于空间的担忧。所以在实现顺序表的时候就使用动态顺序表。

注意:如果上面代码看不懂的话可以去看看博主写的C语言有关结构体,内存函数,指针的内容,因为数据结构就是使用他们来实现的!!!

4,动态顺序表的实现

首先需要定义一个顺序表在哪定义呢?

数据结构中的结构就是结构体,当然要在结构体内部定义了。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//定义一个顺序表
typedef int SLtype;
typedef struct sqelist
{SLtype* arr;//顺序表底层的数组int size;//size用于记录有效数据个数int capacity;//capacity用于管理空间
}SL;

下面是在定义时需要注意的一些细节:
在这里插入图片描述

定义好了顺序表之后我们就来说说增,删,查,改的实现。

增(插入数据):头插,尾插,在指定位置前后插入数据。
删(删除数据):头删,尾删,在指定位置前后删除数据。

查(查找数据):查找指定位置的数据。
改(修改数据):修改指定位置的数据。

在进行这些操作之前我们要对顺序表进行初始化。

//初始化结构体
void SLinit(SL*ps);
//初始化
void SLinit(SL* ps)
{ps->arr = NULL;ps->size = ps->capacity = 0;
}

1,

在插入数据之前我们首先要检查一下,顺序表的空间是否足够不够的话就扩容。
只要是设计插入,就要考虑空间是否足够的问题 所以我们将检查空间是否够封装成一个函数如下:

//判断内存是否足够 封装成一个函数
void SLCheckCapacity(SL*ps);
//检查空间是否足够
void SLCheckCapacity(SL* ps)
{if (ps == NULL){perror("init fial!");return;}//判断空间是否足够 空间不足需要扩容 扩容需要定义一个新的容量 因为capacity总是指向顺序表最后一个元素的后面if (ps->size == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//创建一个临时指针指向扩展空间的首地址SLtype* tmp = (SLtype*)realloc(ps->arr, newcapacity * sizeof(SLtype));//扩容的是新空间//判断是否开辟成功if (tmp == NULL){perror("realloc fail!");return;}//走到这开辟成功ps->arr = tmp;//将新开辟空间的地址给arr 让arr指向新开辟的空间ps->capacity = newcapacity;//更新容量}
}
1,头插

在这里插入图片描述
具体的代码如下:
头文件中:

//头插 
void SLPushFornt(SL* ps, SLtype x);

实现文件中:

//头插
void SLPushFornt(SL* ps, SLtype x)
{//判断ps是否为空指针NULLif (ps == NULL){perror("fail!");return ;}//assert(ps);//如果内存不足SLCheckCapacity(ps);//如果内存足够int i = 0;for (i = ps->size;i > 0;i--){ps->arr[i] = ps->arr[i-1];}//程序走到这说明 ps->arr[0]的位置已经被空出来了ps->arr[0] = x;++ps->size;
}
2,尾插

在这里插入图片描述
头文件中:

//尾插
void SLPushBack(SL* ps, SLtype x);

实现文件中:

//尾插
void SLPushBack(SL* ps, SLtype x)
{if (ps == NULL){perror("init fial!");return;}//判断空间是否足够 空间不足需要扩容 扩容需要定义一个新的容量 因为capacity总是指向顺序表最后一个元素的后面if (ps->size == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//创建一个临时指针指向扩展空间的首地址SLtype* tmp = (SLtype*)realloc(ps->arr, newcapacity * sizeof(SLtype));//扩容的是新空间//判断是否开辟成功if (tmp == NULL){perror("realloc fail!");return ;}//走到这开辟成功ps->arr = tmp;//将新开辟空间的地址给arr 让arr指向新开辟的空间ps->capacity = newcapacity;//更新容量}//空间足够的情况下ps->arr[ps->size++] = x;//等价于 ps->arr[ps->size]=x;size++ 这两段代码
}
2,在指定位置前插入数据

在这里插入图片描述

//在指定位置之前插入数据
void SLInsert(SL* ps, SLtype pos, SLtype x);
//在指定位置插入数据
void SLInsert(SL* ps, SLtype pos, SLtype x)
{//判断ps是否为空指针NULLif (ps == NULL){perror("fail!");return ;}//assert(ps);// 如果内存不足SLCheckCapacity(ps);//如果空间足够for (int i = ps ->size;i > pos;i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;//将空出来的位置给xps->size = ps->size++;//插入数据 size要++ 即有效数据要++
}

2,

1,头删

在这里插入图片描述

//头删
void SLPopFornt(SL* ps);
//头删
void SLPopFornt(SL* ps)
{//判断ps是否为空指针NULLif (ps == NULL){perror("fail!");return ;}//assert(ps);for (int i = 0;i < ps->size;i++){ps->arr[i] = ps->arr[i + 1];}ps->size = ps->size--;
}
2,尾删

在这里插入图片描述

//尾删
void SLPopBack(SL* ps);
//尾删
void SLPopBack(SL* ps)
{//判断ps是否为空指针NULLif (ps == NULL){perror("fail!");return ;}//assert(ps);ps->size = --ps->size;
}
3,删除pos位置(指定位置)的数据

在这里插入图片描述

//删除pos位置的数据
void SLErase(SL* ps, SLtype pos, SLtype x);
/删除pos位置的数据
void SLErase(SL* ps, SLtype pos, SLtype x)
{//判断ps是否为空指针NULLif (ps == NULL){perror("fail!");return;}//assert(ps);// 如果内存不足SLCheckCapacity(ps);for (int i = pos;i < ps->size;i++){ps->arr[i] = ps->arr[i + 1];}ps->size = ps->size--;
}

3,

查找的思路很简单,就是遍历循序表看能否找到我们给定的数据。

//查找
int SLFind(SL* ps, SLtype x);
//查找
int SLFind(SL* ps, SLtype x)
{//判断ps是否为空指针NULLif (ps == NULL){perror("fail!");return;}//assert(ps);//遍历数组查找for (int i = 0;i < ps->size;i++){if (ps->arr[i] == x){//进来说明找到了return i;}}//到这说明没找到return -1;
}

上面函数的返回值之所以是int是为了在测试文件中使用该函数的返回值去判断是否找到了。

4,

修改的话很简单,根据从查找的方法我们找到指定位置后就可以直接修改数据。代码很简单这里就不再展示。

5,销毁

我们在C语言内存函数中说过,在堆区申请的空间不使用时要释放掉避免造成空间的浪费,动态顺序表是使用malloc开辟的所以不使用时要释放掉。

//销毁
void Destroy(SL* ps);
//销毁
void Destroy(SL* ps)
{assert(ps);if (ps->arr)//如果ps—>的arr不为空指针{free(ps->arr);//则释放}ps->arr = NULL;//释放完后arr变成了野指针 及时将其置为空指针NULLps->size = ps->capacity = 0;//释放内存以后顺序表将不复存在 所以将有效数据个数 容量都置为0
}

二,顺序表的总结和问题的思考

时间复杂度:中间/头部的插⼊删除,时间复杂度为O(N)

关于增容的问题:增容需要申请新空间,拷⻉数据,释放旧空间。会有不小的消耗。

增容的注意事项:增容⼀般是呈2倍的增长,势必会有⼀定的空间浪费。例如当前容量 为100,满了以后增容到200,
我们再继续插⼊了5个数据,后⾯没有数据插入了,那么就浪费了95个数据空间。

为了解决以上空间浪费,和增容和时间复杂度优化的问题我们就引出了链表。

三,链表

1,什么是链表?

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

光从概念上去看链表还是不好理解,举个例子:链表就是像是一个火车由一个火车头连接着很多节车厢;每节车厢之间都是独立的;且增加或减少车厢对其他的车厢都没有影响。

在这里插入图片描述

通过上图我们还发现链表与顺序不一样的点就是链表元素之间是指向关系,而顺序表底层是数组,数组是一块连续的空间。而链表就不一样了,链表在逻辑上是线性的也就是我们人为将他看作是连续的;但在物理空间上链表就是不连续的了。
在这里插入图片描述

2,什么是结点?

上图我们看到了链表的结构,链表是由头和结点(车厢)组成的,那么什么是结点呢?

结点就是我们人为的在操作系统的堆区开辟的一块空间,这块空间有连个组成部分一是保存的数据,二是保存下一个结点的地址(指针变量)。

有了上面的认识我们就可以给出结点对应的代码:


typedef struct SListNode  //加上typdef修改名字 可以简化代码
{ //结点的两个组成部分 1,保存的数据 2,保存下一个结点的地址int data; //结点数据 struct SListNode* next; //指针变量⽤保存下⼀个结点的地址
}SL;//将struct ListNode 改名为SL

对于结点(结构体)的解释:
当我们想要保存⼀个整型数据时,实际是向操作系统申请了⼀块内存,这个内存不仅要保存整型数据,也需要保存下⼀个结点的地址(当下⼀个结点为空时保存的地址为空)。
当我们想要从第⼀个结点走到最后一个结点时,只需要在当前结点拿上下一个结点的地址就可以了。

而头结点从图上看就是只存储下一个结点的地址,不存储数据的一个结构体。

从上面的结构我们可以看出链表与顺序表有明显的差异了,顺序表底层使用的是数组;链表使用的是指针。

有了上面的理解,如果给定一个链表让我们打印我们如何实现呢?
在这里插入图片描述

//打印函数
void STLPrint(STLNode* phead)
{STLNode* pcur = phead;//这里重复定义一个pcur是为了避免phead的值发生该变 便于找到链表的头(火车头)while (pcur){printf("%d -> ", pcur->date);pcur = pcur->next;//打印完第一个结构体的date数据 就让pcur指向下一个结构体}//走到这里说明pcur走到了尽头 此时pcur存的是NULL空指针printf("NULL\n");
}

3,链表的分类

链表的结构⾮常多样,以下情况组合起来就有8种(2 x 2 x 2)链表结构:
在这里插入图片描述
虽然链表有这么多结构但是最常用的有两种单链表(不带头单向不循环链表)和双向链表(双向带头循环链表)。

我们先从单链表开始讲起,下面我们就来讲讲怎么实现单链表:

4,单链表的实现

单链表与顺序表一样,也能实现对数据的增,删,查,改等功能,要实现这些功能我们首先要创建一个链表,在我们定义好结点(结构体)后我们就可以人为的往里面放数据和地址,如下图:
先定义好结点:

//定义链表的结构
typedef int STLDateType;
typedef struct SListNode
{STLDateType date;struct SListNode* next;   //指向下一个结构体的指针
}STLNode;

在这里插入图片描述
创建好了链表以后我们就可以来实现增,删,查,改等功能了。

1,

在增之前我们肯定要考虑空间问题,由于单链表是动态申请内存的,不会浪费空间需要增加结点的时候才开辟空间,所以我们要设计一个动态申请内存的函数,方便在增加数据的函数中调用。

//定义一个SLTLbuyNode函数用于动态申请内存
STLNode* STLbuyNode(STLDateType x)
{STLNode* NewNode = (STLNode*)malloc(sizeof(STLNode));//开辟一个结点大小的空间  结点->结构体if (NewNode == NULL){perror("malloc fail!");exit(1);}//走到这说明开辟结点成功了 将新结点的成员初始化NewNode->date = x;//x为我们想要传入的值NewNode->next = NULL;//由于不知道后面还有没有结构体 所以默认置为空指针return NewNode;//返回新创建好的结点的地址
}

有了这个函数我们就可以开始增加数据了:

1,头插

在这里插入图片描述
在.H文件中

//头插
void STLPushFront(STLNode** pphead, STLDateType x);

在.c文件中

//头插
void STLPushFront(STLNode** pphead, STLDateType x)
{assert(pphead);//如果链表为空 则改变链表的值STLNode*NewNode= STLbuyNode(x);NewNode->next = *pphead;//这是将新的结点与老的第一个结点相连接 *pphead = NewNode;//相连之后让*phead走到前面指向刚创建好的新结点  相当于让pphead往前走}
2,尾插

在这里插入图片描述

尾插这里需要注意到底是传值还是传地址,其实大家在目前阶段可以直接记住只要形参的改变不影响实参就传值,否则就传地址。

//尾插
void STLPushBack(STLNode** pphead, STLDateType x);
//尾插
void STLPushBack(STLNode** pphead, STLDateType x)//由于传进来的plist是一级指针 所以要用二级指针来接收
{//注意phead传进来的是链表的头节点STLNode* NewNode = STLbuyNode(x);//直接调用STLbuyNode函数创建一个新的结点//链表为空的情况  此时头结点就应该是我们插入的新结点if (*pphead == NULL){//要想该百年实从参还得是使用指针 传址调用*pphead = NewNode;//这种 变化也是无效的变化 因为我们的目的是要让我们传进来的plist 指向我们创建好的这个新结点 作为第一个结点//phead = NewNode;//下面这种方法是错误的 空链表内什么都没有 哪里来的next成员//phead->next = NewNode;}else{//链表不为空的情况STLNode* ptail = *pphead;//定义一个找尾的指针 让他找到最后一个结点的最后一个成员while (ptail->next != NULL){ptail = ptail->next;}//走到这说明 ptail已经为最后一个结点的next成员了 要将ptail与新结点链接起来ptail->next = NewNode;//NewNode里边存了新结点的地址}
}
3,在指定位置之前插入数据

在这里插入图片描述

//在指定位置前插入数据
void STLInsert(STLNode** pphead, STLNode*Pos,STLDateType x);
//在指定位置之前插入数据
void STLInsert(STLNode** pphead, STLNode* pos, STLDateType x)
{assert(pphead && pos && *pphead);//当pos指向第一个结点 就是头插if (pos == *pphead){STLPushFront(*pphead,x);}//开辟一个新结点STLNode* NewNode = STLbuyNode(x);STLNode* prev = *pphead;//定义一个prev来遍历链表//找到pos的前一个结点while (prev->next != pos)//注意这里只是判断{prev = prev->next;}//走到这里 此时prev->next成员指向的是pos位置之后的结点的地址//走到这里prev->next==pos 注意这里是prev的成员指向了pos位置 说明prev已经走到了pos的前一个结点的位置prev->next=NewNode;NewNode->next=pos;
}
4,在指定位置之后插入数据

在这里插入图片描述

//在指定位置之后插入数据
void STLInsertAfter(STLNode* pos, STLDateType x);
//在指定位置之后插入数据
void STLInsertAfter(STLNode* pos, STLDateType x)
{assert(pos);STLNode* NewNode = STLbuyNode(x);//pos NewNode pos->nextNewNode->next = pos->next;pos->next = NewNode;
}

1,

1,头删

在这里插入图片描述

//头删
void STLPopFront(STLNode** pphead);
//头删
void STLPopFront(STLNode** pphead)
{assert(pphead && *pphead);STLNode* next = (*pphead)->next;//定义一个next指针来保存*pphead的next成员的值free(*pphead);*pphead = next;
}
2,尾删

在这里插入图片描述

//尾删
void STLPopBack(STLNode** pphead);
//尾删
void STLPopBack(STLNode** pphead)
{assert(pphead && *pphead);////只有一个结点的情况if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}//有多个结点的情况else{STLNode* ptail = *pphead;STLNode* prev = NULL;//定义一个prev跟在ptail的后面while (ptail->next){prev = ptail;ptail = ptail->next;}//循环结束时,ptail为NULL,prev指向最后一个结点prev->next = NULL;//将倒数第二个结点的next指针置为空 free(ptail);//释放原本最后一个结点(结构体)的空间ptail = NULL;//最后一个结点被释放了 避免野指针所以置为空}
}
3,删除pos位置之前的结点

在这里插入图片描述

//删除pos位置的结点
void STLErase(STLNode** pphead, STLNode* pos);
//删除pos位置的结点
void STLErase(STLNode** pphead, STLNode* pos)
{assert(pphead && pos);//判断prev是不是指向头结点if (pos == *pphead){STLPopFront(pphead);}else{STLNode* prev = *pphead;while (prev->next != pos)//注意循环条件{prev = prev->next;}//走到这 prev就已经找到pos前一个位置了prev->next = pos->next;//将prev位置处的结点与pos后的结点相连free(pos);pos = NULL;//防止pos为野指针}
}
4,删除pos位置之后的结点

在这里插入图片描述

//删除pos之后的结点
void SLTEraseAfter(SLTNode* 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;
}

3,

在这里插入图片描述

//查找数据
STLNode* STLFind(STLNode* phead, STLDateType x);
//查找数据
STLNode* STLFind(STLNode* phead, STLDateType x)
{STLNode* pcur = phead;while (pcur->next != NULL)//让pcur去遍历链表{if (pcur->date == x){return pcur;}pcur = pcur->next;//让pcur偏移}return NULL;
}

4 ,链表的销毁

链表是我们人为开辟的空间,所以为了避免空间的浪费在我们不使用链表时就手动将他销毁。

在这里插入图片描述

//销毁链表
void SListDestroy(SLTNode** pphead);
//销毁链表
void SListDestroy(SLTNode** pphead)
{SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

以上就是所有单链表的实现了,由于篇幅有限剩下的双链表我们下期再介绍。

以上就是本章的全部内容啦!
最后感谢能够看到这里的读者,如果我的文章能够帮到你那我甚是荣幸,文章有任何问题都欢迎指出!制作不易还望给一个免费的三连,你们的支持就是我最大的动力!
在这里插入图片描述


http://www.hkcw.cn/article/vjOrohbjQU.shtml

相关文章

【笔记】在 MSYS2(MINGW64)中安装 Python 工具链的记录

#工作记录 &#x1f4cc; 安装背景 操作系统&#xff1a;MSYS2 MINGW64当前时间&#xff1a;2025年6月1日Python 版本&#xff1a;3.12&#xff08;默认通过 pacman 安装&#xff09;目标工具链&#xff1a; pipxnumpypipsetuptoolswheel &#x1f6e0;️ 安装过程与结果记录…

sqli-labs靶场32-37关(宽字节注入)

目录 前言 less32&#xff08;宽字节注入&#xff09; less33&#xff08;宽字节注入&#xff09; less34&#xff08;POST型宽字节注入&#xff09; less35&#xff08;数字型闭合宽字节&#xff09; less36&#xff08;宽字节注入&#xff09; less37&#xff08;POST…

SRE 基础知识:在站点可靠性工程中可以期待什么

作者&#xff1a;来自 Elastic Elastic Observability Team 在过去的 20 年里&#xff0c;大多数领先企业已经采用云计算和分布式系统来开发它们的应用程序。一个意想不到的后果是&#xff1a;传统的 IT 运维&#xff08; IT operations - ITOps &#xff09;常常难以应对日益增…

day16 leetcode-hot100-31(链表10)

25. K 个一组翻转链表 - 力扣&#xff08;LeetCode&#xff09; 1.模拟法 思路 将这个过程拆解为两个步骤&#xff0c;第一步将单分组的节点反转&#xff0c;第二步将反转后的链表加入原链表。 针对节点反转很容易&#xff0c;参考之前的206. 反转链表 - 力扣&#xff08;Le…

黑马Java面试笔记之Redis篇(使用场景)

1.面试题 我看你做的项目中&#xff0c;都用到了redis&#xff0c;你在最近的项目中那些场景使用了redis呢 2.提问的底层逻辑 面试官提问你这个问题一是想验证你的项目场景的真实性&#xff0c;二是为了作为深入发问的切入点 3.延伸出来的知识点 3.1 缓存 缓存三兄弟&#x…

PyTorch -TensorBoard的使用 (一)

设置环境 新建python文件 .py 安装Tensorboard 在终端进行安装 显示安装成功 两个logs&#xff0c;出现这种情况怎么解决 所有的logs文件删掉delete&#xff0c;重新运行 add_image 不满足要求 Opencv-numpy 安装Opencv add_image 用法示例 &#xff08;500&#xff0c;375&am…

解决Ubuntu20.04上Qt串口通信 QSerialPort 打开失败的问题

运行Qt串口通信 open(QIODevice::ReadWrite) 时&#xff0c;总是失败。 1、打印失败原因 QString QSerialHelper::openSerail() {if(this->open(QIODevice::ReadWrite) true){return this->portName();}else{return "打开失败";//return this->errorStri…

[yolov11改进系列]基于yolov11引入迭代注意力特征融合iAFF的python源码+训练源码

【iAFF介绍】 1. IAFF&#xff08;迭代注意力特征融合&#xff09; iAFF通过引入多尺度通道注意力模块和迭代融合&#xff0c;更好的整合不同尺度和语义不一致的特征&#xff0c;有效解决特征融合问题&#xff0c;提高目标检测的精度。 特征融合&#xff0c;即不同层或分支的…

springboot-响应接收与ioc容器控制反转、Di依赖注入

1.想将服务器中的数据返回给客户端&#xff0c;需要在controller类上加注解&#xff1a;ResponseBody; 这个注解其实在前面已经使用过&#xff0c;RestController其实就包含两个注解&#xff1a; Controller ResponseBody 返回值如果是实体对象/集合&#xff0c;将会转换为j…

idea中springboot2.7(由于步入另一个线程,已跳过 xxx 处的断点)

idea springboot2.7 debug 问题 springboot 2.7 debug 模式时引入 spring-boot-devtools 卡在代码中不往下执行&#xff0c;提示&#xff1a;由于步入另一个线程&#xff0c;已跳过 xxx 处的断点。 原因 springboot 2.7 引入 spring-boot-devtools <!-- debug时不推荐开…

ROS应用之如何配置RTOS满足机器人系统中的实时要求

如何配置RTOS以满足机器人系统中的实时要求 前言 实时操作系统&#xff08;RTOS&#xff09;在机器人系统中的应用至关重要&#xff0c;尤其在需要对环境变化做出快速反应的高精度控制系统中。ROS2作为开源机器人操作系统&#xff0c;为机器人提供了强大的框架和工具链&#x…

03 APP 自动化-定位元素工具元素定位

文章目录 一、Appium常用元素定位工具1、U IAutomator View Android SDK 自带的定位工具2、Appium Desktop Inspector3、Weditor安装&#xff1a;Weditor工具的使用 4、uiautodev通过定位工具获取app页面元素有哪些属性 二、app 元素定位方法 一、Appium常用元素定位工具 1、U…

数学分析——一致性(均匀性)和收敛

目录 1. 连续函数 1.1 连续函数的定义 1.2 连续函数的性质 1.2.1 性质一 1.2.2 性质二 1.2.3 性质三 1.2.4 性质四 2. 一致连续函数 2.1 一致连续函数的定义 2.2 一致连续性定理(小间距定理)(一致连续函数的另一种定义) 2.3 一致连续性判定法 2.4 连…

并发执行问题 (上)

S3和S4 没法区别 i存在 父进程数据区 子进程存在数据区 所以返回值不一样 通过返回值运行不一样的代码 原来的进程镜像作废,运行的执行新的elf文件

一周学会Pandas2之Python数据处理与分析-数据重塑与透视-pivot_table() - 透视表 (长 -> 宽,支持聚合)

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili pivot_table() 是 pandas 中最强大的数据透视工具&#xff0c;它不仅能重塑数据&#xff0c;还能进行复杂的数据聚合…

郑钦文抢七险胜萨姆索诺娃 首次闯进法网八强

6月1日,在2025年法国网球公开赛女单第四轮比赛中,赛会8号种子、中国选手郑钦文以2:1战胜俄罗斯选手萨姆索诺娃,首次闯进法网女单八强。这场比赛耗时2小时47分钟,双方在场上展开了激烈的争夺。首盘比赛双方陷入苦战,郑钦文通过更深的落点限制对手进攻,并且在多拍回合中展现…

robot_lab学习笔记【MDP综述】

文章目录 整体介绍第一部分第二部分第三部分总结 整体介绍 在robot_lab中的mdp文件夹下面包含6个文件&#xff1a;[init.py , commands.py , curiculums.py, events.py , observations.py , rewards.py ] 对每个部分的详细讲解在总结中会指向子链接 init.py文件的代码如下 …

Leetcode 2093. 前往目标城市的最小费用

1.题目基本信息 1.1.题目描述 一组公路连接 n 个城市&#xff0c;城市编号为从 0 到 n - 1 。 输入包含一个二维数组 highways &#xff0c;其中 highways[i] [city1i, city2i, tolli] 表示有一条连接城市 city1i 和 city2i 的双向公路&#xff0c;允许汽车缴纳值为 tolli 的…

【C++】模板与特化技术全面教程(claude sonnet 4)

第一章&#xff1a;模板的基础概念 (Template Fundamentals) 1.1 什么是模板&#xff1f; 模板 (Template) 是C中的一种泛型编程 (Generic Programming) 机制&#xff0c;它允许我们编写与类型无关的代码。想象一下&#xff0c;如果我们要为不同的数据类型编写相同逻辑的函数&a…

VBA数据库解决方案二十:Select表达式From区域Where条件Order by

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…