顺序表和链表(详解)

线性表

线性表( linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
注意我们学习数据结构也就是学习增删查改,后期会围绕这四个字进行讲解,本文的讲解会把基础模拟实现部分讲的很透彻,算法oj题可以基于基础知识向外扩展,有了基础数据结构理解相信算法oj题也很好拿下了

顺序表

2.1 概念及结构
顺序表是用一段 物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
1. 静态顺序表:使用定长数组存储元素
2. 动态顺序表:使用动态开辟的数组存储。
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

接口实现

我们一般需要定义3个变量,数组,容量和实际数据数量,因为要方便后续扩容操作

下面我会把每个接口的意义讲解,这部分需要结构体和动态内存的基础,我前边讲的很详细,可以去看看每个函数都会断言,所以我们不会每个都讲,但是大家一定要明白断言的意义,一般是防止对空指针操作和检查越界

顺序表的初始化,默认先开辟一块4个整形(我们假设存储整形)的空间,size和capacity置为0。

销毁,先释放malloc的空间,将指针置空,容量和size置0

打印数据,也就是遍历这个数组很简单,用下标访问数据

检查扩容操作,当实际数据数量等于容量,要扩容,当我们插入数据时,先判断是否需要扩容,再插入,这样就能实现简易版的vector了(注意要断言是因为防止传空指针)
pos是要插入的下标,end最后一个数据下标,size是实际数据个数
插入逻辑,要记得断言下标要大于等于0并且小于等于size,下标是0相当于头插,下标是size相当于尾插,这部分一定要画图,画图以后这个插入逻辑也就是挪动覆盖,下标<size是随机插入,下标=size是尾插
删除逻辑,断言+挪动数据覆盖逻辑

 

尾插很简单,(注释的部分和没注释的是两个版本,可以用常规思路扩容+尾插,也可以直接复用插入逻辑,把参数改成最后一个下标就行)

尾删,要记得先断言检查下标,有效数据个数一定要大于0才能尾删,也可以直接复用删除逻辑

头插很简单,可以直接复用插入逻辑,参数给0就行

头删也很简单,复用删除逻辑

修改逻辑很简单,一行代码,可是为什么这一行代码也封装成一个函数呢,因为我们最好不要直接访问数组数据,缺少断言检查,用这个函数可以防止我们传越界的参数

以上就是关于顺序表实现的全部逻辑,希望能对大家有用

顺序表实现的完整代码

seqlist.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>// 静态顺序表
//#define N 1000
//typedef int SLDataType;
//
//struct SeqList
//{
//	SLDataType a[N];
//	int size;
//};// 动态顺序表
typedef int SLDataType;typedef struct SeqList
{SLDataType* a;int size;        // 存储有效数据个数int capacity;    // 空间大小
}SL;// 管理数据 -- 增删查改
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
void SLCheckCapacity(SL* ps);// 头插头删 尾插尾删
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);// 返回下标,没有找打返回-1
int SLFind(SL* ps, SLDataType x);// 在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x);
// 删除pos位置的值
void SLErase(SL* ps, int pos);void SLModify(SL* ps, int pos, SLDataType x);

seqlist.c

#include"seqlist.h"void SLInit(SL* ps)
{assert(ps);ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);if (ps->a == NULL){perror("malloc failed");exit(-1);//return;}ps->size = 0;ps->capacity = 4;
}void SLDestroy(SL* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->size = 0;
}void SLPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; i++){printf("%d ", ps->a[i]);}printf("\n");
}void SLCheckCapacity(SL* ps)
{assert(ps);// 满了要扩容if (ps->size == ps->capacity){SLDataType* tmp = (SLDataType*)realloc(ps->a, ps->capacity * 2 * (sizeof(SLDataType)));if (tmp == NULL){perror("realloc failed");exit(-1);}ps->a = tmp;ps->capacity *= 2;}
}// 17:25继续
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);/*SLCheckCapacity(ps);ps->a[ps->size] = x;ps->size++;*/SLInsert(ps, ps->size, x);
}void SLPopBack(SL* ps)
{assert(ps);// 温柔的检查//if (ps->size == 0)//return;// 暴力的检查//assert(ps->size > 0);ps->a[ps->size - 1] = 0;//ps->size--;SLErase(ps, ps->size - 1);
}void SLPushFront(SL* ps, SLDataType x)
{assert(ps);//SLCheckCapacity(ps);挪动数据//int end = ps->size - 1;//while (end >= 0)//{//	ps->a[end + 1] = ps->a[end];//	--end;//}//ps->a[0] = x;//ps->size++;SLInsert(ps, 0, x);
}void SLPopFront(SL* ps)
{assert(ps);/*assert(ps->size > 0);int begin = 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}ps->size--;*/SLErase(ps, 0);
}int SLFind(SL* ps, SLDataType x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (ps->a[i] == x){return i;}}return -1;
}// 在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);int end = ps->size - 1;while (end >= pos){ps->a[end + 1] = ps->a[end];--end;}ps->a[pos] = x;ps->size++;
}// 删除pos位置的值
void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);int begin = pos + 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}ps->size--;
}void SLModify(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos < ps->size);ps->a[pos] = x;
}

还有一些测试用例,感兴趣的可以尝试运行一下,已经测试过了代码都是对的

#include<stdio.h>
#include"seqlist.h"void TestSeqList1()
{SL sl;SLInit(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushBack(&sl, 4);SLPushBack(&sl, 5);SLPushBack(&sl, 6);SLPushBack(&sl, 6);SLPushBack(&sl, 0);SLPushBack(&sl, 0);SLPrint(&sl);SLPopBack(&sl);SLPopBack(&sl);SLPrint(&sl);SLPopBack(&sl);SLPopBack(&sl);SLPopBack(&sl);SLPopBack(&sl);SLPopBack(&sl);SLPopBack(&sl);SLPopBack(&sl);//SLPopBack(&sl);//SLPopBack(&sl);/*SLPopBack(&sl);SLPopBack(&sl);SLPopBack(&sl);SLPopBack(&sl);*/SLPrint(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPrint(&sl);SLDestroy(&sl);
}void TestSeqList2()
{SL sl;SLInit(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushBack(&sl, 4);SLPushBack(&sl, 5);SLPrint(&sl);SLPushFront(&sl, 10);SLPushFront(&sl, 20);SLPushFront(&sl, 30);SLPushFront(&sl, 40);SLPrint(&sl);SLDestroy(&sl);
}void TestSeqList3()
{SL sl;SLInit(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushBack(&sl, 4);SLPushBack(&sl, 5);SLPrint(&sl);SLPopFront(&sl);SLPopFront(&sl);SLPrint(&sl);SLPopFront(&sl);SLPopFront(&sl);SLPopFront(&sl);//SLPopFront(&sl);SLPrint(&sl);SLPushBack(&sl, 4);SLPushBack(&sl, 5);SLPrint(&sl);SLDestroy(&sl);
}void TestSeqList4()
{SL sl;SLInit(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushBack(&sl, 4);SLPushBack(&sl, 5);SLPushFront(&sl, -1);SLPushFront(&sl, -2);SLPrint(&sl);SLInsert(&sl, 3, 40);SLPrint(&sl);int x;scanf("%d", &x);int pos = SLFind(&sl, x);if (pos != -1){SLInsert(&sl, pos, x * 10);}SLPrint(&sl);SLDestroy(&sl);}void TestSeqList5()
{SL sl;SLInit(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushBack(&sl, 4);SLPushBack(&sl, 5);SLPrint(&sl);SLErase(&sl, 2);SLPrint(&sl);int x;scanf("%d", &x);int pos = SLFind(&sl, x);if (pos != -1){SLErase(&sl, pos);}SLPrint(&sl);SLDestroy(&sl);}void TestSeqList6()
{SL sl;SLInit(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushBack(&sl, 4);SLPushBack(&sl, 5);SLPrint(&sl);SLModify(&sl, 2, 20);sl.a[2] = 20;SLPrint(&sl);/*int x;scanf("%d", &x);int pos = SLFind(&sl, x);if (pos != -1){SLModify(&sl, pos, x*10);}SLPrint(&sl);*/int pos, x;scanf("%d%d", &pos, &x);//sl.a[pos] = x;SLModify(&sl, pos, x);SLPrint(&sl);SLDestroy(&sl);
}void TestSeqList7()
{SL sl;SLInit(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushBack(&sl, 4);SLPushBack(&sl, 5);SLPrint(&sl);SLPopFront(&sl);SLDestroy(&sl);
}int main()
{TestSeqList1();return 0;
}

相关oj题

27. 移除元素 - 力扣(LeetCode)

26. 删除有序数组中的重复项 - 力扣(LeetCode)

88. 合并两个有序数组 - 力扣(LeetCode)

顺序表的讲解基本结束,还有顺序表和链表的对比,在STL我已经写过了,可以看看是一个道理

链表

链表的概念及结构
概念:链表是一种 物理存储结构上非连续、非顺序的存储结构,数据元素的 逻辑顺序是通过链表中的 指针链接次序实现的 。
现实中链表存放的都是malloc出来的地址,是随机值,但是也不排除会有这种很规律的取值
我们要注意单链表是没有哨兵位,且只有一个next后继指针,头指针存放第一个节点的地址,后边的内存块如图所示,每个内存块存放有下一个内存块的地址,最后一个内存块存放NULL(这个是规则)

链表的分类

别看有这么多种链表,其实实际常用的也就是单链表和双向带头循环链表
其实学会这两种链表就行,单链表适合做oj题,双向链表适合存储数据
带头双向循环链表由哨兵位,哨兵位可以理解为不存有效数据的第一个头结点,遍历只会遍历有效数据结点,只有一个哨兵位等价于链表为NULL
单链表没有哨兵位头结点,只有头指针,所以我们要控制头指针一直指向第一个节点,就要用二级指针进行管理

单链表的实现

 

注意单链表部分一定要学会传二级指针,因为我们要改变结构体指针的内容(当链表为空),因为没有哨兵位,只有一个头指针,后边的带头双向循环链表由哨兵位的头结点,就不用传二级指针了(单链表的增删查改的情况有些多,大致分为链表为空或者链表非空来讨论)

链表内部存储两个数据,一个是值x,一个是结构体指针*next(用来存放下一个结点的地址)

打印操作,参数是结构体变量的地址,拿到数据向后遍历,最后一个数据next为空,停下来

这个函数用来申请一个新节点的空间,并且初始化为NULL和x,类似c++的new

尾插操作,参数要传二级指针(也就是头结点指针的地址,这样在链表第一个节点为空的时候可以直接尾插)

链表不为空,要多定义一个尾指针,遍历链表到最后一个,将NULL改为新new处结点的地址,就完成链表的插入了(道理很简单,实在没想明白可以画图,数据结构部分就是要多画图才能学好)

头删很简单,先断言plist不能是NULL,空链表没必要删除了

先保存要删除结点的下一个结点地址,然后再free掉要删除的节点,并且让头指针指向新的首结点

尾删结点,是空链表或者只有一个结点的链表很简单,当两个以上的时候定义一个前驱指针tailprev,遍历链表,当tail的next是NULL的时候,跳出循环并且free掉尾指针指向的结点,将前驱指针的next置空,这样就完成了尾删很简单

头插很简单,先new(并不是c++的new,只是我想这样写)一个新节点,将新节点的next存放第一个结点的地址,plist存放新节点的地址,就完成头插

查询某个值,很简单遍历一下,找到就返回下标,没找到就返回NULL

在pos位置之前插入,如果我们要在第一个结点前插入,直接复用头插逻辑,其他情况我们要定义一个前驱指针prev,用来指向pos位置之前的结点,当prev指向要插入位置之前结点的时候,跳出循环,链接数据。很简单

删除pos位置的值,和插入逻辑相似,删除第一个节点直接复用头删逻辑,其他情况(因为要删除后边的某个节点)定义一个前驱指针保存删除节点前一个位置,当prev指向pos之前的结点时,链接要删除节点左右节点,删除pos结点并且置空(也可以不置空,因为栈帧到此会销毁结束,我们也访问不到了)

以上就是我对单链表的理解,希望能帮到大家

单链表完整源码

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLTDataType;typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;void SLTPrint(SLTNode* phead);SLTNode* BuySListNode(SLTDataType x);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//SLTNode* SLTPushBack(SLTNode* phead, SLTDataType x);void SLTPushFront(SLTNode** pphead, SLTDataType x);void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);SLTNode* SLTFind(SLTNode* phead, SLTDataType x);// 在pos之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos);

#include"slist.h"
void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;//while (cur != NULL)while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}SLTNode* BuySListNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail");exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}// 16:07继续
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySListNode(x);if (*pphead == NULL){// 改变的结构体的指针,所以要用二级指针*pphead = newnode;}else{SLTNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}// 改变的结构体,用结构体的指针即可tail->next = newnode;}
}void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySListNode(x);newnode->next = *pphead;*pphead = newnode;
}void SLTPopBack(SLTNode** pphead)
{assert(pphead);// 1、空assert(*pphead);// 2、一个节点// 3、一个以上节点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* tailPrev = NULL;SLTNode* tail = *pphead;while (tail->next){tailPrev = tail;tail = tail->next;}free(tail);//tail = NULL;tailPrev->next = NULL;}
}void SLTPopFront(SLTNode** pphead)
{assert(pphead);// 空assert(*pphead);// 非空SLTNode* newhead = (*pphead)->next;free(*pphead);*pphead = newhead;
}SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);if (pos == *pphead){SLTPushFront(pphead, x);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}SLTNode* newnode = BuySListNode(x);prev->next = newnode;newnode->next = pos;}
}// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead);assert(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;}
}

#define _CRT_SECURE_NO_WARNINGS 1
#include"slist.h"void TestSList1()
{int n;printf("请输入链表的长度:");scanf("%d", &n);printf("\n请依次输入每个节点的值:");SLTNode* plist = NULL;for (size_t i = 0; i < n; i++){int val;scanf("%d", &val);SLTNode* newnode = BuySListNode(val);// 头插newnode->next = plist;plist = newnode;}SLTPrint(plist);SLTPushBack(&plist, 10000);SLTPrint(plist);
}void TestSList2()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPushBack(&plist, 5);SLTPrint(plist);SLTPushFront(&plist, 10);SLTPushFront(&plist, 20);SLTPushFront(&plist, 30);SLTPushFront(&plist, 40);SLTPrint(plist);
}void TestSList3()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPushBack(&plist, 5);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);SLTPopBack(&plist);SLTPrint(plist);// SLTPopBack(&plist);// SLTPrint(plist);
}void TestSList4()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPushBack(&plist, 5);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);SLTPrint(plist);SLTPopFront(&plist);//SLTPopFront(&plist);SLTPrint(plist);
}void TestSList5()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPushBack(&plist, 5);SLTPrint(plist);SLTNode* pos = SLTFind(plist, 40);if (pos){pos->data *= 10;}SLTPrint(plist);int x;scanf("%d", &x);pos = SLTFind(plist, x);if (pos){SLTInsert(&plist, pos, x * 10);}SLTPrint(plist);
}int main()
{TestSList1();return 0;
}

双向链表的实现

带头双向循环链表,有前驱指针,后继指针和数据

最基础的申请新结点空间,赋值并且置空,后期new的雏形,申请空间+初始化都干了

这个是哨兵位头结点,会返回给外边的plist用来控制增删查改逻辑

打印逻辑,遍历链表并且打印值

在某个位置之前插入

pos之前插入很简单,定义一个posprev前驱指针,将newnode链接上去

牛逼的地方是,传哨兵位的下一个节点也就是头结点的指针,就成为头插了;传哨兵位指针,完成尾插,因为哨兵位的前一个节点就是整个链表最后一个数据

尾删

头删

删除逻辑很简单将要删除结点的前结点和后结点保存,删除pos位置节点,然后链接左右节点

尾插直接复用插入逻辑,传哨兵位指针过去就行,完成尾插,当然也可以自己实现,但是建议直接复用,节省代码

头插也就是在哨兵位下一个之前插入,直接复用

尾部删除,注意要断言(,当只有哨兵位结点时不能删除,此时有效数据为0),直接复用删除逻辑

头删直接复用删除逻辑,删除哨兵位下一个结点就是头删

最后还有一个求有效数据个数的代码,很简单,遍历链表到哨兵位结束也就是计算了有效数据(除哨兵位)

以上就是我对双向链表的理解,很透彻希望对大家有用

双向链表模拟实现完整代码

#include"List.h"LTNode* BuyLTNode(LTDataType x)
{LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL){perror("malloc fail");exit(-1);}node->data = x;node->next = NULL;node->prev = NULL;return node;
}LTNode* LTInit()
{LTNode* phead = BuyLTNode(0);phead->next = phead;phead->prev = phead;return phead;
}void LTPrint(LTNode* phead)
{assert(phead);printf("phead<=>");LTNode* cur = phead->next;while (cur != phead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");
}void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTInsert(phead, x);
}void LTPopBack(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTErase(phead->prev);
}void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTInsert(phead->next, x);
}void LTPopFront(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTErase(phead->next);
}int LTSize(LTNode* phead)
{assert(phead);int size = 0;LTNode* cur = phead->next;while (cur != phead){++size;cur = cur->next;}return size;
}// pos֮ǰx
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* newnode = BuyLTNode(x);posPrev->next = newnode;newnode->prev = posPrev;newnode->next = pos;pos->prev = newnode;
}// ɾposλ
void LTErase(LTNode* pos)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* posNext = pos->next;free(pos);posPrev->next = posNext;posNext->prev = posPrev;
}

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;
typedef struct ListNode
{struct ListNode* next;struct ListNode* prev;LTDataType data;
}LTNode;LTNode* BuyLTNode(LTDataType x);
LTNode* LTInit();
void LTPrint(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);int LTSize(LTNode* phead);LTNode* LTFind(LTNode* phead, LTDataType x);// pos֮ǰx
void LTInsert(LTNode* pos, LTDataType x);
// ɾposλ
void LTErase(LTNode* pos);

#include"List.h"void TestList1()
{LTNode* plist = LTInit();LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);LTPushBack(plist, 5);LTPrint(plist);LTPushFront(plist, 10);LTPushBack(plist, 10);LTPrint(plist);
}void TestList2()
{LTNode* plist = LTInit();LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);LTPushBack(plist, 5);LTPrint(plist);LTPopBack(plist);LTPopFront(plist);LTPrint(plist);LTPopFront(plist);LTPopFront(plist);LTPopFront(plist);//LTPopFront(plist);LTPrint(plist);
}void TestList3()
{LTNode* plist = LTInit();LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);LTPushBack(plist, 5);LTPrint(plist);LTPushFront(plist, 10);LTPushFront(plist, 20);LTPushFront(plist, 30);LTPushFront(plist, 40);LTPrint(plist);
}void TestList4()
{LTNode* plist = LTInit();LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);LTPushBack(plist, 5);LTPrint(plist);LTPopFront(plist);LTPrint(plist);LTPopBack(plist);LTPrint(plist);
}int main()
{TestList4();return 0;
}

链表面试oj题

203. 移除链表元素 - 力扣(LeetCode)

206. 反转链表 - 力扣(LeetCode)

876. 链表的中间结点 - 力扣(LeetCode)

21. 合并两个有序链表 - 力扣(LeetCode)

链表分割_牛客题霸_牛客网

链表的回文结构_牛客题霸_牛客网

160. 相交链表 - 力扣(LeetCode)

141. 环形链表 - 力扣(LeetCode)

142. 环形链表 II - 力扣(LeetCode)

138. 随机链表的复制 - 力扣(LeetCode)

题目有点多,都是经典链表的题,可以研究一下(有了基础内容的讲解,算法题可以尝试完成)

顺序表和链表的区别

打对钩的是优点,错误是缺点,可以了解一下,因为各有各的优势,在不同场景要用不同的数据结构

以上就是我对数据结构链表和顺序表的全部理解,以后会创作更多用心作品(目录有对应的模块,需要什么看什么)

感谢大家的支持!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/67998.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 解析 HTML 实战指南

在网页开发和数据处理的场景中&#xff0c;经常需要从 HTML 文档里提取有用的信息。C# 作为一门强大的编程语言&#xff0c;提供了丰富的工具和库来实现 HTML 的解析。这篇博客就带你深入了解如何使用 C# 高效地解析 HTML。 一、为什么要在 C# 中解析 HTML 在实际项目中&…

Spring 是如何解决循环依赖问题

Spring 框架通过 三级缓存 机制来解决循环依赖问题。循环依赖是指两个或多个 Bean 相互依赖&#xff0c;形成一个闭环&#xff0c;例如 Bean A 依赖 Bean B&#xff0c;而 Bean B 又依赖 Bean A。Spring 通过提前暴露未完全初始化的 Bean 来解决这个问题。 以下是 Spring 解决…

vxe-table和element表尾合计行

1、vxe-table vxe-table的表尾合计&#xff0c;需要show-footer和footer-method搭配使用。 <vxe-table:data"tableData"ref"vxeRef"border resizable :footer-method"footerMethod":show-footer"true" >…

监控与调试:性能优化的利器 — ShardingSphere

在分布式数据库系统中&#xff0c;监控和调试是确保系统高效运行的关键。ShardingSphere 提供了多种监控和调试工具&#xff0c;帮助开发者实时跟踪和优化性能&#xff0c;识别瓶颈&#xff0c;进行故障排查&#xff0c;从而提升系统的稳定性和响应速度。本文将介绍如何使用 Sh…

上位机知识篇---ROS2命令行命令静态链接库动态链接库

文章目录 前言第一部分&#xff1a;ROS2命令行命令1. 基础命令&#xff08;1&#xff09;ros2 run&#xff08;2&#xff09;ros2 launch&#xff08;3&#xff09;ros2 node&#xff08;4&#xff09;ros2 topic&#xff08;5&#xff09;ros2 service&#xff08;6&#xff0…

Browser-Use WebUI项目启动指南

摘要 此前发布《Browser - Use WebUI 使用体验》博文后&#xff0c;鉴于部分朋友运行时出现问题&#xff0c;重新运行并整理相关内容。本文详细记录 Web UI 项目启动全过程&#xff0c;涵盖 Python 3.11、Chrome 浏览器及 API Keys 等环境要求&#xff0c;Python 环境检查、依赖…

leetcode 面试经典 150 题:有效的括号

链接有效的括号题序号20题型字符串解法栈难度简单熟练度✅✅✅ 题目 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须…

Grafana系列之Dashboard:新增仪表板、新增变量、过滤变量、变量查询、导入仪表板、变量联动、Grafana Alert

概述 关于Prometheus和Grafana的安装&#xff0c;略过。 写在前面 Dashboard&#xff1a;仪表板&#xff0c;可包含多个PanelPanel&#xff1a;面板&#xff0c;Dashboard中的组件 如有写得不对的地方&#xff0c;烦请指出。 新增仪表板 点击右上角的 选择New dashboard…

使用 Ansys Discovery 对离心风机进行仿真

了解设置模拟并获得有用结果的步骤。 离心风机&#xff1a;基础知识和重要性 离心风机&#xff0c;也称为径流式风机&#xff0c;是旨在通过将动能转化为势能来增加空气或气体的压力和流量的机械装置。它们的工作原理是利用旋转叶轮产生的离心力轴向吸入空气&#xff0c;然后…

客户案例:向导ERP与金蝶云星空集成方案

一、客户背景 该客户公司主要致力于黄金、铂金、金镶玉首饰的研发设计、生产加工、批发及直营加盟业务。公司总部占地面积目前已达6000多平方米&#xff0c;拥有标准生产厂房和现代化生产设施&#xff0c;拥有一支完善的企业管理团队和专业技工队伍。 该企业目前同时采用向导 E…

mac 通过 Homebrew 安装 git 遇到的问题

问题真多啊 &#xff01;&#xff01;&#xff01; 解决方式 见 1. / 2. / 3 . / 4. / 5. remote: Enumerating objects: 290323, done. remote: Counting objects: 100% (473/473), done. remote: Compressing objects: 100% (253/253), done. error: RPC failed; curl 92 H…

springboot 引入 单元测试 @Test

springboot版本 2.6 引入依赖 <dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.8.1</version><scope>test</scope></dependency>写测试类 package com.mv.m…

机器学习-K近邻算法

文章目录 一. 数据集介绍Iris plants dataset 二. 代码三. k值的选择 一. 数据集介绍 鸢尾花数据集 鸢尾花Iris Dataset数据集是机器学习领域经典数据集&#xff0c;鸢尾花数据集包含了150条鸢尾花信息&#xff0c;每50条取自三个鸢尾花中之一&#xff1a;Versicolour、Setosa…

【豆包MarsCode蛇年编程大作战】花样贪吃蛇

目录 引言 展示效果 prompt提示信息 第一次提示&#xff08;实现基本功能&#xff09; 初次实现效果 第二次提示&#xff08;美化UI&#xff09; 第一次美化后的效果 第二次美化后的效果 代码展示 实现在线体验链接 码上掘金使用教程 体验地址&#xff1a; 花样贪吃蛇…

小白爬虫——selenium入门超详细教程

目录 一、selenium简介 二、环境安装 2.1、安装Selenium 2.2、浏览器驱动安装 三、基本操作 3.1、对页面进行操作 3.1.1、初始化webdriver 3.1.2、打开网页 3.1.3、页面操作 3.1.4、页面数据提取 3.1.5、关闭页面 ?3.1.6、综合小案例 3.2、对页面元素进行操作 3…

U3D的.Net学习

Mono&#xff1a;这是 Unity 最初采用的方式&#xff0c;它将 C# 代码编译为中间语言 (IL)&#xff0c;然后在目标平台上使用虚拟机 (VM) 将其转换为本地机器码执行。 IL2CPP&#xff1a;这是一种较新的方法&#xff0c;它会将 C# 代码先编译为 C 代码&#xff0c;再由 C 编译器…

Java集合学习:HashMap的原理

一、HashMap里的Hash是什么&#xff1f; 首先&#xff0c;我们先要搞清楚HashMap里的的Hash是啥意思。 当我们在编程过程中&#xff0c;往往需要对线性表进行查找操作。 在顺序表中查找时&#xff0c;需要从表头开始&#xff0c;依次遍历比较a[i]与key的值是否相等&#xff…

SOAFEE 技术研讨会:汽车软件定义与自动驾驶技术探讨

在本次技术研讨会上&#xff0c;来自汽车与科技领域的专家们围绕汽车软件定义及自动驾驶技术展开了深入交流与探讨。从 SOAFEE 蓝图计划的创新性理念&#xff0c;到 Autoware 开源项目及 Open AD Kit 在实际应用中的探索&#xff0c;再到 Edge Workload Abstraction and Orches…

FastJson很快,有什么用?

FastJson 在国内的热度还是挺高的&#xff0c;受到了很多开发者的喜欢。不过&#xff0c;我自己倒没有在项目中用过。我记得刚工作那会新做的一个项目有明确规定禁止使用 FastJson。 昨天看到一篇关于 FastJson 的文章&#xff0c;这位朋友分享了自己在使用 FastJson 遇到的一…

react antd点击table单元格文字下载指定的excel路径

在使用 Ant Design (antd) 的 Table 组件时&#xff0c;如果想点击表格单元格中的文字来触发下载指定路径的 Excel 文件&#xff0c;可以通过以下步骤实现&#xff1a; 1. 确保有一个可供下载的 Excel 文件&#xff1a;需要有一个服务器端点或者一个可以直接访问的 URL&#xf…