本篇是博主在学习数据结构时的心得,希望能够帮助到大家,也许有些许遗漏,但博主已经尽了最大努力打破信息差,如果有遗漏还请见谅,嘻嘻,前路漫漫,我们一起前进!!!!
今天是七夕!!!虽然博主没有女朋友,但是在此我也祝各位有情人终成眷属。爱永无止境。
希望你们都能遇到自己的米子哈和大kin库!!!!!!!!!!!!!!!!!!!!!!!

目录
链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的 指针链接次序实现的
链表中每个结点都是独⽴申请的(即需要插⼊数据时才去申请⼀块结点的空间),我们需要通过指针变量来保存下⼀个结点位置才能从当前结点找到下⼀个结点。
- 链式机构在逻辑上是连续的,在物理结构上不⼀定连续
- 结点⼀般是从堆上申请的
- 从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续
单链表申请空间:
- struct SListNode
- {
- int data;
- SListNode*next;
- }
在我们想要存储一个整形数据时,实际先向内存申请一块空间,这个空间不仅要存放当前结点的数据,还存放着下一个结点的地址。(最后的结点存储的地址为NULL)
图解

单链表包括创建、申请结点、打印、头插、尾插、头删、尾删、链表查找、指定前插入、指定后插入、删除pos结点、删除pos后结点、销毁链表
单链表的实现 :
- typedef int SLDataType
- typedef struct SListNode
- {
- SLDataType Data;
- struct SListNode*next;
- }sL;
-
以上代码是对链表结点结构的声明,该链表结点包括一个类型为SLDataType的整形数据和一个地址。
- SL*SLTButNode(SLTDataType x)
- {
- SL*newnode=(SL*)malloc(sizeof(SL));
- if(newnode==NULL)
- {
- perror("malloc fail");
- exit(1);
- }
- newnode->data=x;
- newnode->next=NULLl;
- }
我们用图表示一下:

- void SLprint(SL*phead)
- {
- SL*pucr=phead;
- while(pcur)
- {
- printf("%d",pucr->next);
-
- }
- printf("NULL/n");
-
- }
这下我们用图解:

这里有几个注意的点:
1.*phead一定是该链表中的第一个结点。
2.对pcur解引用拿到下一个结点的指针才能进入下一次循环,不然pcur走不动,打印陷入死循环。
- void SLTPushBack(SL**pphead,SLTDataType x)
- {
- assert(pphead);
- SL*newnode=SLButNode(x);
- if(*pphead==NULL)
- {
- *pphead=newnode;
-
- }
- else
- {
- SL*pcur=*pphead;
- while(pcur->next)
- {
- pucr=pucr->next;
- }
- pcur->next=newnode;
-
- }
如果*pphead是NULL时,说明该链表的头结点还没有建立,所以如果从对一个没有头节点的链表进行尾插,头节点就是尾插的目标。
当*pphead不是NULL的时,代表要在链表结点之后插入,为了更直观的感受,我们用图解的方式来解释:

- void SLTPushFront(SL**pphead,SLDataType x)
- {
- assert(pphead&&*pphead);
- SL*newnode=SLBuynode(x);
-
- newnode->next=*pphead;
- *pphead=newnode;
-
-
- }
头插相较于尾插简单的多:
只要将先将头结点赋值给新的结点的存储地址,然后在再将新节点newnode当作新的头结点*pphead即可;

- void SLTpopBack(SL**pphead)
- {
- //链表为空:不可以删除
- assert(pphead && *pphead);
- //处理只有一个结点的情况:要删除的就是头结点
- if((*pphead)->next=NULL)
- {
- free(*pphead);
- *pphead=NULL;
- }
- else
- {
- SL* ptail = *pphead;
- SL* prev = NULL;
- while (ptail->next)
- {
- prev = ptail;
- ptail = ptail->next;
- }
- prev->next = NULL;
- free(ptail);
- ptail = NULL;
-
- }
这里我们还是用图解的方式来讲解:
尾删详解:这里用prev作为ptail的前一个指针,当ptail找到最后一个结点的时候,prev就是尾删的尾结点,所以先将prev的存储的下一个结点地址(prev->next)置为NULL,再将ptail所指的空间释放,最后ptail指向NULL,这就是尾删的过程

- void SLTPopFront(SLTNode** pphead)
- {
- assert(pphead && *pphead);
-
- SL* next = (*pphead)->next;
- //*pphead --> next
- free(*pphead);
- *pphead = next;
- }
这里头删就更加简单了,头结点就是要删除的结点,直接将头结点的指向的下一个结点的地址存储到next中,在进行释放头结点空间,最后在对新结点进行操作

- SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
- {
- assert(phead);
- SLTNode* pcur = phead;
- while (pcur)
- {
- if (pcur->data == x)
- {
- return pcur;
- }
- pcur = pcur->next;
- }
- //没有找到
- return NULL;
- }
查找详解:
- 我们用pcur指针对单链表进行遍历,遍历过程中如果找到是否有与x匹配的数据,说明找到了,返回与x相等数据的pcur指针。
- 用pcur遍历完之后,如果没有找到与之匹配的数据,说明没有找到,返回NULL 。

- void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
- {
- assert(pphead);
- assert(pos);
-
- if (pos == *pphead)
- {
- SLTPushFront(pphead, x);
- }
- else
- {
- SLTNode* newnode = SLTBuyNode(x);
- //找prev :pos的前一个结点
- SLTNode* prev = *pphead;
- while (prev->next != pos)
- {
- prev = prev->next;
- }
-
- //prev newnode --> pos
- newnode->next = pos;
- prev->next = newnode;
- }
- }
插入(位置前)详解:
- 如果pos的位置在头结点,我们直接在头结点头插即可。
- 如果pos不在头结点,我们先创建一个新的结点,用prev指针找到pos的前一个结点的地址,pos的本质是一个地址,我们用将新节点存储的地址给pos,newnode的地址给到prev指针所指向结点的存储地址。这样我们就将新节点插入到了pos结点的前面

- void SLTInsertAfter(SLTNode* pos, SLTDataType x)
- {
- assert(pos);
- SLTNode* newnode = SLTBuyNode(x);
- //pos newnode --> pos->next
- newnode->next = pos->next;
- pos->next = newnode;
- }
插入(位置后)详解:
在指定位置后插入数据不需要用循环找pos后的位置了,因为pos所在的结点存储的地址就是下一个结点的地址,所以将pos存储的下一个结点的地址赋值给newnode的存储地址,之后再将pos的存储地址给newnode的地址,这样就完成了位置后插入。

- void SLTErase(SLTNode** pphead, SLTNode* pos)
- {
- assert(pphead && *pphead);
- assert(pos);
-
- //头删
- if (pos == *pphead)
- {
- SLTPopFront(pphead);
- }
- else
- {
- SLTNode* prev = *pphead;
- while (prev->next != pos)
- {
- prev = prev->next;
- }
- //prev pos pos->next
- prev->next = pos->next;
- free(pos);
- pos = NULL;
- }
- }
- //删除pos之后的结点
- void SLTEraseAfter(SLTNode* pos)
- {
- assert(pos && pos->next);
-
- //pos pos->next pos->next->next
- SLTNode* del = pos->next;
- pos->next = pos->next->next;
- free(del);
- del = NULL;
- }
删除pos结点详解:
删除pos结点:用prev指针找到pos前一个结点的位置,用prev所在的结点的存储地址改为pos->next(pos后一个结点)。之后释放pos结点的空间,最后将pos指针置为NULL
删除pos之后的结点:用del代表pos的下一个结点,将pos的下下个结点的地址给pos的存储地址,释放del,将del指针置为NULL;
- void SListDestroy(SLTNode** pphead)
- {
- assert(pphead && *pphead);
-
- SLTNode* pcur = *pphead;
- while (pcur)
- {
- SLTNode* next = pcur->next;
- free(pcur);
- pcur = next;
- }
- *pphead = NULL;
- }
- #define _CRT_SECURE_NO_WARNINGS 1
- #include"Slist.h"
- void SLprint(SLTNode*phead)
- {
- SLTNode* pcur = phead;
- while (pcur)
- {
- printf("%d", pcur->data);
-
- }
- 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;
- }
-
-
-
- void SLTPushBack(SLTNode** pphead, SLTDataType x)
- {
- assert(pphead);
- SLTNode* newnode = SLTBuyNode(x);
- if (pphead == NULL)
- {
- *pphead = newnode;
-
- }
- else
- {
- //找尾结点
- SLTNode* pcur = *pphead;
- while (pcur->next)
- {
- pcur = pcur->next;
- }
- //pcur newnode
- pcur->next = newnode;
- n }
- }
- void SLTPushFront(SLTNode** pphead, SLTDataType x)
- {
- assert(pphead);
-
- SLTNode* newnode = SLTBuyNode(x);
- //newnode *pphead
- newnode->next = *pphead;
- *pphead = newnode;
- }
- void SLTPopBack(SLTNode** pphead)
- {
- //链表为空:不可以删除
- assert(pphead && *pphead);
- //处理只有一个结点的情况:要删除的就是头结点
- if ((*pphead)->next == NULL)
- {
- free(*pphead);
- *pphead = NULL;
- }
- else
- {
- //找 prev ptail
- SLTNode* ptail = *pphead;
- SLTNode* prev = NULL;
- while (ptail->next)
- {
- prev = ptail;
- ptail = ptail->next;
- }
- prev->next = NULL;
- free(ptail);
- ptail = NULL;
- }
-
- }
-
- void SLTPopFront(SLTNode** pphead)
- {
- assert(pphead && *pphead);
-
- SLTNode* next = (*pphead)->next;
- //*pphead --> next
- free(*pphead);
- *pphead = next;
- }
-
- 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(SLTNode** pphead, SLTNode* pos, SLTDataType x)
- {
- assert(pphead);
- assert(pos);
-
- if (pos == *pphead)
- {
- SLTPushFront(pphead, x);
- }
- else
- {
- SLTNode* newnode = SLTBuyNode(x);
- //找prev :pos的前一个结点
- SLTNode* prev = *pphead;
- while (prev->next != pos)
- {
- prev = prev->next;
- }
-
- //prev newnode --> pos
- newnode->next = pos;
- prev->next = newnode;
- }
- }
- //在指定位置之后插⼊数据
- void SLTInsertAfter(SLTNode* pos, SLTDataType x)
- {
- assert(pos);
- SLTNode* newnode = SLTBuyNode(x);
- //pos newnode --> pos->next
- newnode->next = pos->next;
- pos->next = newnode;
- }
- //删除pos结点
- void SLTErase(SLTNode** pphead, SLTNode* pos)
- {
- assert(pphead && *pphead);
- assert(pos);
-
- //头删
- if (pos == *pphead)
- {
- SLTPopFront(pphead);
- }
- else
- {
- SLTNode* prev = *pphead;
- while (prev->next != pos)
- {
- prev = prev->next;
- }
- //prev pos pos->next
- prev->next = pos->next;
- free(pos);
- pos = NULL;
- }
- }
- //删除pos之后的结点
- void SLTEraseAfter(SLTNode* pos)
- {
- assert(pos && pos->next);
-
- //pos pos->next pos->next->next
- SLTNode* del = pos->next;
- pos->next = pos->next->next;
- free(del);
- del = NULL;
- }
- //销毁链表
- void SListDestroy(SLTNode** pphead)
- {
- assert(pphead && *pphead);
-
- SLTNode* pcur = *pphead;
- while (pcur)
- {
- SLTNode* next = pcur->next;
- free(pcur);
- pcur = next;
- }
- *pphead = NULL;
- }
.h文件
- #pragma once
- #include
- #include
- #include
-
- //定义链表(结点)的结构
-
- typedef int SLTDataType;
-
- typedef struct SListNode {
- SLTDataType data;
- struct SListNode* next;
- }SLTNode;
-
- void SLTPrint(SLTNode* phead);
-
- //插入
- void SLTPushBack(SLTNode** pphead, SLTDataType x);//尾插
- void SLTPushFront(SLTNode** pphead, SLTDataType x);//头插
-
- //删除
- void SLTPopBack(SLTNode** pphead);
- void SLTPopFront(SLTNode** pphead);
-
- //查找
- 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);
-
本篇单链表的章节到这里就结束了..........
感谢各位能观看到这里,感谢大家支持博主,很开心能和大家一起共同进步,一起学习!!!!!我希望能让更多的人看到这篇文章,希望大家能够多点赞,多交流。可以点波收藏,忘记的时候可以观看,在此wheel down先谢过大家!!!!!
时间漫长,我们一起度过,前路未知,我们一起并肩。
