当前位置:   article > 正文

[C语言][数据结构][链表] 双链表的从零实现!

[C语言][数据结构][链表] 双链表的从零实现!

目录

零.必备知识

0.1 一级指针 && 二级指针

0.2 双链表节点的成员列表

        a. 数据

        b. 后驱指针

        c. 前驱指针

0.3 动态内存空间的开辟

一. 双链表的实现与销毁

        1.1 节点的定义

        1.2 双向链表的初始化 && 创建新节点

        1.3 尾插 

        1.4 头插 

        1.5 尾删 

        1.6 头删

        1.7 在指定位置之后插入数据 

        1.8 删除指定位置的数据 

        1.9 销毁双链表

 二.双链表源码

List.h

List.c


零.必备知识

0.1 一级指针 && 二级指针

0.2 双链表节点的成员列表

        a. 数据

        b. 后驱指针

        c. 前驱指针

0.3 动态内存空间的开辟


一. 双链表的实现与销毁

此次实现的链表是带头双向循环链表.如图:

        1.1 节点的定义

后驱节点指向写一个节点.

前驱节点指向前一个节点.

        1.2 双向链表的初始化 && 创建新节点

初始化是指:创建了一个哨兵节点.

这个哨兵节点的作用是:避免双向链表死循环.

 

        1.3 尾插 

贯穿本文的核心要点是:先修改新节点中前驱,后驱指针的指向.再修改其余节点前驱,后驱指针的指向.

        1.4 头插 

 

        1.5 尾删 

尾删和接下来的头删,都是可以创建一个临时指针来指向要删除的节点.

这样看以来更直观,更重要的是方便进行空间的释放.

 

        1.6 头删

 

 

        1.7 在指定位置之后插入数据 

在指定位置之后插入数据:可以先实现一个查找函数,来自己指定pos.

 

        1.8 删除指定位置的数据 

 

        1.9 销毁双链表

 注意:此处传入的是一级指针,并没有真正的修改phead(哨兵位/头节点)中的值,如果要真正的销毁双链表,需要传入二级指针.

那么为什么我传的是一级指针呢?  答案:保持传入参数的一致性.

 

 二.双链表源码

List.h

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <assert.h>
  5. // 单个节点的定义
  6. typedef int LTDateType;
  7. typedef struct List
  8. {
  9. LTDateType date;
  10. struct List* next; // 后驱节点
  11. struct List* prev; // 前驱节点
  12. }List;
  13. // 节点的创建
  14. List* LTBuyNode(LTDateType x);
  15. // 双向链表的初始化
  16. List* LTInit();
  17. // 双向链表的展示
  18. void LTPrint(List* phead);
  19. // 尾插-头插
  20. void LTPushBack(List* phead, LTDateType x);
  21. void LTPushFront(List* phead, LTDateType x);
  22. // 尾删-头删
  23. void LTPopBack(List* phead);
  24. void LTPopFront(List* phead);
  25. // 查找指定数据
  26. List* LTFind(List* phead, LTDateType x);
  27. // 在指定位置之后插入数据
  28. void LTInsertAfter(List* pos, LTDateType x);
  29. // 删除指定位置的数据
  30. void LTErase(List* phead, List* pos);
  31. // 销毁双链表
  32. void LTDestroy(List* phead);

List.c

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include "List.h"
  3. // 节点的创建
  4. List* LTBuyNode(LTDateType x)
  5. {
  6. List* newNode = (List*)malloc(sizeof(List));
  7. if (newNode == NULL) { // 创建失败
  8. perror("malloc fail!");
  9. exit(1);
  10. }
  11. newNode->date = x;
  12. newNode->next = NULL;
  13. newNode->prev = NULL;
  14. return newNode;
  15. }
  16. // 双向链表的初始化
  17. List* LTInit()
  18. {
  19. List* newNode = LTBuyNode(-1);
  20. newNode->next = newNode;
  21. newNode->prev = newNode;
  22. return newNode;
  23. }
  24. // 双向链表的展示
  25. void LTPrint(List* phead)
  26. {
  27. assert(phead);
  28. List* pcur = phead->next;
  29. while (pcur != phead) {
  30. printf("%d->", pcur->date);
  31. pcur = pcur->next;
  32. }
  33. printf("\n");
  34. }
  35. // 尾插
  36. void LTPushBack(List* phead, LTDateType x)
  37. {
  38. assert(phead);
  39. // 创建新节点
  40. List* newNode = LTBuyNode(x);
  41. // 先更改新节点的前驱后驱指针
  42. newNode->next = phead;
  43. newNode->prev = phead->prev;
  44. // 更改其余节点的前驱后驱指针
  45. phead->prev->next = newNode;
  46. phead->prev = newNode;
  47. }
  48. // 头插
  49. void LTPushFront(List* phead, LTDateType x)
  50. {
  51. assert(phead);
  52. List* newNode = LTBuyNode(x);
  53. // 更改newNode的前驱后驱指针
  54. newNode->next = phead->next;
  55. newNode->prev = phead;
  56. // 更改其余节点的指针指向
  57. phead->next->prev = newNode;
  58. phead->next = newNode;
  59. }
  60. // 尾删
  61. void LTPopBack(List* phead)
  62. {
  63. assert(phead);
  64. List* pcur = phead->prev;
  65. // 更改要删除的节点的前一个节点的指针指向
  66. pcur->prev->next = phead;
  67. // 更改哨兵位的指针指向
  68. phead->prev = pcur->prev;
  69. // 释放尾节点
  70. free(pcur);
  71. pcur = NULL;
  72. }
  73. // 头删
  74. void LTPopFront(List* phead)
  75. {
  76. assert(phead);
  77. List* pcur = phead->next;
  78. phead->next = pcur->next;
  79. pcur->next->prev = phead;
  80. // 销毁pcur节点
  81. free(pcur);
  82. pcur = NULL;
  83. }
  84. // 查找指定数据
  85. List* LTFind(List* phead, LTDateType x)
  86. {
  87. assert(phead);
  88. List* pcur = phead->next;
  89. while (pcur != phead) {
  90. if (pcur->date == x) {
  91. printf("找到了!\n");
  92. return pcur;
  93. }
  94. pcur = pcur->next;
  95. }
  96. printf("没有找到!\n");
  97. return NULL;
  98. }
  99. // 在指定位置之后插入数据
  100. void LTInsertAfter(List* pos, LTDateType x)
  101. {
  102. assert(pos);
  103. List* newNode = LTBuyNode(x);
  104. // 修改新节点的指向
  105. newNode->next = pos->next;
  106. newNode->prev = pos;
  107. // 修改其余节点的指向
  108. pos->next->prev = newNode;
  109. pos->next = newNode;
  110. }
  111. // 删除指定位置的数据
  112. void LTErase(List* phead, List* pos)
  113. {
  114. assert(phead && pos);
  115. pos->next->prev = pos->prev;
  116. pos->prev->next = pos->next;
  117. // 销毁pos节点
  118. free(pos);
  119. pos = NULL;
  120. }
  121. // 销毁双链表
  122. void LTDestroy(List* phead)
  123. {
  124. assert(phead);
  125. List* pcur = phead->next;
  126. while (pcur != phead) {
  127. List* prev = pcur;
  128. pcur = pcur->next;
  129. // 销毁 pcur的前一个节点
  130. free(prev);
  131. prev = NULL;
  132. }
  133. // 销毁哨兵位
  134. free(phead);
  135. phead = NULL; // 但是没有真正的改变哨兵位, 因为传的是一级指针
  136. // 如果要真正的改变 phead的值,则需要传递二级指针
  137. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/477811
推荐阅读
相关标签
  

闽ICP备14008679号