关键词搜索

源码搜索 ×
×

线性表之单链表

发布2022-03-17浏览897次

详情内容

链表

1.链表的概念及结构

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

物理上:1.链式结构在逻辑上是连续的,但在物理上不一定连续。

2.现实中的节点一般都是从对上申请出来的。

3.从对上申请空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续。

2.链表的分类

1.单向或者双向

2.带头或者不带头

3.循环或者非循环

共计8种

最常用的两种链表:无头单向非循环链表  和  带头双向循环链表

1.无头单向非循环链表结构简单,一般不会单独用来存数据。实际更多是作为其他数据结构的子结构,如:哈希桶,图的邻链表。另外在笔试中出现很多。

2.带头双向循环链表:结构最复杂,一般用于单独存储数据。实际中使用的链表结构,都涉及带头双向循环链表。另外,这个结构看似复杂,实际使用代码实现会带来很多优势,实现反而更简单。

3.链表的实现

无头单向非循环链表

建立结构体变量

  1. typedef int SLDataType;//方便更改数据类型
  2. typedef struct SList
  3. {
  4. SLDataType data;
  5. struct SList* next;//节点,存下一个链表的地址。
  6. }SList;

链表不需要单独封装一个函数初始化

1.链表打印

基本思路:1)断言指针

2)为空直接打印NULL

3)创建临时变量,判断条件 cur!=NULL

  1. void SListPrint(SList* phead)
  2. {
  3. if (phead == NULL)
  4. printf("NULL\n");
  5. else
  6. {
  7. SList* cur = phead;//一般情况下不要动头节点。
  8. while (cur != NULL)//为空结束循环
  9. {
  10. printf("%d->", cur->data);
  11. cur = cur->next;
  12. }
  13. printf("NULL\n");
  14. }
  15. }

2.创建一个新的节点

后续插入数据都需要创建新新节点,独立封装一个函数是最优解。

基本思路:1)malloc节点,判断节点是否成功创建,否就直接终止程序。

2)结构体成员初始化。

  1. SList* BuySListNode(SLDataType x)
  2. {
  3. //创建一个节点
  4. SList* tmp = (SList*)malloc(sizeof(SList));
  5. if (tmp == NULL)
  6. {
  7. printf("malloc fail\n");
  8. exit(-1);//直接退出程序
  9. }
  10. else
  11. {
  12. SList* newnode = tmp;
  13. //初始化
  14. newnode->data = x;
  15. newnode->next = NULL;
  16. return newnode;
  17. }
  18. }

2.尾插数据

基本思路:1)创建一个新节点

2)传递指针要传递二级指针,创建变量创建的是结构体指针,修改数据要在地址上修改。

3)判断链表是否为空,为空直接赋值,不为空找到为节点,链接上即可。

配图:

代码实现:

  1. void SListPushBack(SList** pphead, SLDataType x)
  2. {
  3. //不需要断言。
  4. //创建一个节点。
  5. SList* newnode = BuySListNode(x);
  6. //如果为空,直接赋值
  7. if (*pphead == NULL)
  8. {
  9. *pphead = newnode;
  10. }
  11. else
  12. {
  13. //遍历链表,找到尾节点
  14. SList* tail = *pphead;
  15. while (tail->next != NULL)//下一个节点为空,上一个节点就是尾节点。
  16. {
  17. tail = tail->next;//找下一个节点
  18. }
  19. tail->next = newnode;
  20. //新节点初始化时指针是指NULL,可以不用二次链接
  21. }
  22. }

 

3.头插数据

基本思路:1)创建新节点

2)判断空与非空,是否需要额外讨论。

画图:

1)非空

2)空

两者无区别

代码:

  1. void SListPushFront(SList** pphead, SLDataType x)
  2. {
  3. SList* newnode = BuySListNode(x);
  4. //plist非空
  5. newnode->next = *pphead;
  6. *pphead = newnode;
  7. //为空时不需要额外讨论,画图解释。
  8. }

 

4.尾删数据

基本思路:1)断言

2)链表为空,直接返回

3)找到尾节点,free掉

i.只有2个节点,尾删就是置空头节点。

ii.多个节点,采用双指针的方法prev,tail

画图:

1)链表为空 return;

2)链表有2个节点

3)链表有多个节点

prev->next = tail->next(NULL);

free(tail);

代码:

  1. void SListPopBack(SList** pphead)
  2. {
  3. assert(pphead);
  4. //空节点
  5. if (*pphead == NULL)
  6. {
  7. return;
  8. }
  9. else if ((*pphead)->next == NULL)
  10. {
  11. free(*pphead);
  12. *pphead = NULL;
  13. }
  14. //多个节点
  15. else
  16. {
  17. SList* tail = (*pphead)->next;
  18. SList* prev = *pphead;
  19. while (tail->next != NULL)
  20. {
  21. tail = tail->next;
  22. prev = prev->next;
  23. }
  24. free(tail);
  25. prev->next = NULL;
  26. }
  27. }

5.头删数据

基本思路:1)修改传递二级指针

2)如果是空节点直接返回

3)优良二或多个节点,创建一个next指针,保存头节点的

     下一个节点,free掉头节点,将头节点换成next

画图:

当只有一个节点的时候,next= NULL,改变与多个节点的情况相同,不需要额外讨论。

代码:

  1. void SListPopFront(SList** pphead)
  2. {
  3. assert(pphead);
  4. //空节点
  5. if (*pphead == NULL)
  6. {
  7. return;
  8. }
  9. else
  10. {
  11. //两个或多个节点
  12. SList* next = (*pphead)->next;
  13. free(*pphead);
  14. *pphead = next;
  15. }
  16. }

 

6.查找x

基本思路:1)断言,防止传递空指针

2)遍历查找x,找到返回节点,找不到返回NULL

实现代码:

  1. SList* SListFind(SList* phead, SLDataType x)
  2. {
  3. assert(phead);
  4. SList* cur = phead;
  5. while (phead != NULL)
  6. {
  7. if (cur->data == x)
  8. {
  9. return cur;
  10. }
  11. else
  12. cur = cur->next;
  13. }
  14. return NULL;
  15. }

7.在pos后插入x

基本思路:1)断言pos,以防传递空指针

2)创建新的节点newnode

3)进行插入的两种思路

思路一:创建临时变量tmp 保存pos->next节点,随便链接即可。

思路二:直接链接,但是要注意从后往前链接,防止出现连接未完成,pos找不到原先的pos->next。

  1. void SistInsertAfter(SList* pos, SLDataType x)
  2. {
  3. assert(pos);
  4. 方法一:
  5. //SList* tmp = pos->next;//保存节点,以防止pos指向原先的节点失效,引发bug
  6. //SList* newnode = BuySListNode(x);//插入就要创建新的节点
  7. //pos->next = newnode;//pos链接新的节点
  8. //newnode->next = tmp;//实现插入
  9. //方法二:
  10. SList* newnode = BuySListNode(x);
  11. newnode->next = pos->next;
  12. pos->next = newnode;
  13. }

8.在pos前插入x

基本思路:1)断言pos;

      2)创建新的节点

      3)分情况讨论

i若是pos指向的是头节点,那就调用头插函数接口。

ii一般情况,创建一个cur指针变量,找到pos前的节点,进行插入,与7相似的处理手段。

实现代码:

  1. void SListInsertBefore(SList** pphead,SList* pos, SLDataType x)
  2. {
  3. assert(pos);
  4. SList* newnode = BuySListNode(x);
  5. if (pos == *pphead)
  6. {
  7. SListPushFront(pphead,x);
  8. }
  9. else
  10. {
  11. SList* cur = *pphead;
  12. while (cur->next != pos)
  13. {
  14. cur = cur->next;
  15. }
  16. cur->next = newnode;
  17. newnode->next = pos;
  18. }
  19. }

9.删除pos后的节点

基本思路:1)断言pos

2)创建临时节点next,记录pos->next,即要free的节点

3)进行链接pos 和 next->next

4)坑点,若pos后面为空,那么就会出现next->next访问出错,就需要处理下。

使用if语句进行处理。

画图:

代码实现:

  1. void SListPopAfter(SList* pos)
  2. {
  3. assert(pos);
  4. if (pos->next != NULL)//pos后为NULL就不执行代码
  5. {
  6. SList* next = pos->next;
  7. pos->next = next->next;
  8. free(next);
  9. next = NULL;
  10. }
  11. /*else
  12. printf("pos已经是尾节点了\n");*///单纯测试代码
  13. }

 

10.删除pos前的节点

基本思路:1)断言

2)分情况讨论:

i.pos如果是头节点,直接返回

ii.如果是两个节点,那就直接调用头删函数解决

iii.如果是多个节点,创建cur节点,cur->next->next为pos时,就是要找的cur

    free节点。

3)由于使用一般情况的节点数必须超过3个,所以<3的就需要额外讨论。

画图:

i

ii

iii

代码实现:

  1. void SListPopBefore(SList* pos,SList** pphead)
  2. {
  3. assert(pos);
  4. //一个
  5. if (pos == *pphead)
  6. {
  7. return;
  8. }
  9. else if ((*pphead)->next == pos)
  10. {
  11. SListPopFront(pphead);
  12. }
  13. else
  14. {//多个
  15. SList* cur = *pphead;
  16. while (cur->next->next != pos)
  17. {
  18. cur = cur->next;
  19. }
  20. SList* prev = cur->next;
  21. cur->next = pos;
  22. free(prev);
  23. prev = NULL;
  24. }
  25. }

11.删除pos位置的值

基本思路:1)断言pos和pphead

2)分类讨论

i如果pos指向的是头节点,那就调用头删函数

ii创建临时变量cur遍历链表,找到pos前的节点,记为cur,链接cur和pos->next

free掉pos

画图:

代码实现:

  1. void SListPopPos(SList** pphead, SList* pos)
  2. {
  3. assert(pphead&&pos);
  4. if (pos == *pphead)
  5. {
  6. SListPopFront(pphead);
  7. }
  8. else
  9. {
  10. SList* cur = *pphead;
  11. while (cur->next != pos)//找到pos前的节点
  12. {
  13. cur = cur->next;
  14. }
  15. cur->next = pos->next;//链接
  16. free(pos);//释放pos
  17. pos = NULL;
  18. }
  19. }

12.删除链表

基本思路:1)遍历链表,一个节点一个节点的删

2)free前创建临时变量next记录cur->next,循环条件就是cur!=NULL

3)头节点最后置空

  1. void SListDestroy(SList** pphead)
  2. {
  3. SList* cur = *pphead;
  4. while (cur)//一个节点一个节点的删
  5. {
  6. SList* next = cur->next;
  7. free(cur);//释放
  8. cur = next;//循环调整
  9. }
  10. *pphead = NULL;
  11. }

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载