当前位置:   article > 正文

【数据结构】——双链表的实现(赋源码)

【数据结构】——双链表的实现(赋源码)

双链表的概念和结构

双链表的全称叫做:带头双向循环链表

它的结构示意图如下

注意:这⾥的“带头”跟前⾯我们说的单链表的“头结点”是两个概念,实际前⾯的在单链表阶段称呼不严谨,但是为了读者们更好的理解就直接称为单链表的头结点。 

带头链表⾥的头结点,实际为“哨兵位”,哨兵位结点不存储任何有效元素,只是站在这⾥“放哨的”也可以认为是用来占位置滴!!!

双链表的实现

首先先在结构体当中输入需要的数据,则有如下的数据是需要的

结构体中的数据

  1. typedef int LTDataType;//方便对数据类型进行统一的替换
  2. typedef struct ListNode ListNode;//对结构体的名称重新命名交ListNode
  3. struct ListNode
  4. {
  5. LTDataType data;
  6. ListNode* next;
  7. ListNode* prev;
  8. };

则上面的图可以变成这样

双链表新结点的创建及双链表的初始化

  1. ListNode* LTBuyNode(LTDataType x)
  2. {
  3. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));//一个结构体的大小
  4. if (newnode == NULL)
  5. {
  6. perror("malloc fail!");
  7. exit(1);
  8. }
  9. newnode->data = x;
  10. newnode->next = newnode->prev = newnode;
  11. return newnode;//返回头结点
  12. }

链表的初始化需要一个创建的新的结点作为哨兵位

  1. //void LTInit(ListNode** pphead)
  2. //{
  3. // //创建一个头结点即“哨兵位”
  4. // *pphead = LTBuyNode(-1);
  5. //}
  6. ListNode* LTInit()
  7. {
  8. ListNode* phead = LTBuyNode(-1);
  9. return phead;
  10. }
  11. //上面是两种初始化的方法
  12. //第一种需要传递一个二级指针

在上面的代码当中,我们只需要创建一个头结点来保证第一个“头”存在即可。

插入
第一个参数传一级还是二级,要看phead指向的结点会不会改变
如果发生改变,那么pphead的改变要影响实参,传二级
如果不发生改变,pphead不会影响实参,传一级

双链表的尾插

  1. //尾插
  2. void LTPushBack(ListNode* phead, LTDataType x)
  3. {
  4. assert(phead);
  5. //创建需要插入的结点
  6. //上面初始化的newnode是头结点,这个newnode是尾插的结点
  7. ListNode* newnode = LTBuyNode(x);
  8. newnode->next = phead;
  9. newnode->prev = phead->prev;
  10. phead->prev->next = newnode;
  11. phead->prev = newnode;
  12. }

 上面的顺序是不能改变的,否则无法让新结点找到原来链表的位置

这边测试一下我们的尾插代码依次插入1 2 3 4  

 双链表的头插

  1. //头插
  2. void LTPushFront(ListNode* phead, LTDataType x)
  3. {
  4. assert(phead);
  5. ListNode* newnode = LTBuyNode(x);
  6. newnode->next = phead->next;
  7. newnode->prev = phead;
  8. phead->next->prev = newnode;
  9. phead->next = newnode;
  10. }

头插和尾插是类似的 ,不过有一个特殊的地方

头插是头插在哨兵位和第一个真正的结点中间

同样的,上面的顺序位置是不能改变的

测试头插代码

这个代码是在上面尾插代码基础上的操作

 双链表的尾删

  1. //尾删
  2. void LTPopBack(ListNode* phead)
  3. {
  4. assert(phead);
  5. assert(!Empty(phead));
  6. ListNode* del = phead->prev;
  7. ListNode* prev = del->prev;
  8. prev->next = phead;
  9. phead->prev = prev;
  10. free(del);
  11. del = NULL;
  12. }

 这边仍然是在尾插的基础上的操作

这边我们进行了五次尾删,所以代码assert断言了!

将一次尾删注释,下面就是尾删的效果 

双链表的头删 

  1. //头删
  2. void LTPopFront(ListNode* phead)
  3. {
  4. assert(phead);
  5. assert(!Empty(phead));
  6. ListNode* del = phead->next;
  7. del->next->prev = phead;
  8. phead->next = del->next;
  9. free(del);
  10. del = NULL;
  11. }

这个仍然是在尾插的基础上操作的,如果继续删除,跟上面的情况一样assert断言报错 

以上就是最基础的增删 ,下面开始加大难度!

双链表中查找数据pos

  1. //找相同数据
  2. ListNode* LTFind(ListNode* phead, LTDataType x)
  3. {
  4. assert(phead);
  5. ListNode* pcur = phead->next;
  6. while (pcur != phead)
  7. {
  8. if (pcur->data == x)
  9. {
  10. return pcur;
  11. }
  12. pcur = pcur->next;
  13. }
  14. return NULL;
  15. }

这边我们查找的是数据3,所以我们可以找到 

 

这个在链表中没有数据6,所以我们没有找到相关的数据 

 在pos之后插入结点

这个和尾插以及头插其实是类似的,这里主要是寻找到pos结点,然后插入想要的数据

  1. //在pos之后插入结点
  2. void LTInsert(ListNode* pos, LTDataType x)
  3. {
  4.     assert(pos);
  5.     ListNode* newnode = LTBuyNode(x);
  6.     newnode->next = pos->next;
  7.     newnode->prev = pos;
  8.     pos->next->prev = newnode;
  9.     pos->next = newnode;
  10. }

在3的后面插入数据10 

 

删除pos结点 

  1. //删除指定位置节点
  2. void LTErase(ListNode* pos)
  3. {
  4. assert(pos);
  5. // pos->prev pos pos->next
  6. pos->prev->next = pos->next;
  7. pos->next->prev = pos->prev;
  8. free(pos);
  9. pos = NULL;
  10. }

双链表的销毁 

这里我们提供了两种的销毁方法,两种方法基本是类似的

  1. //销毁
  2. void LTDesTroy(ListNode** pphead)
  3. {
  4. assert(pphead && *pphead);
  5. ListNode* pcur = (*pphead)->next;
  6. while (pcur != *pphead)
  7. {
  8. ListNode* next = pcur->next;
  9. free(pcur);
  10. pcur = next;
  11. }
  12. //销毁哨兵位结点
  13. free(*pphead);
  14. *pphead = NULL;
  15. pcur = NULL;
  16. }
  17. //为了更好的记忆,我们让销毁也传递一级指针
  18. void LTDesTroy2(ListNode* phead)//传一级,需要手动将plist置为NULL
  19. {
  20. assert(phead);
  21. ListNode* pcur = phead->next;
  22. while (pcur != phead)
  23. {
  24. ListNode* next = pcur->next;
  25. free(pcur);
  26. pcur = next;
  27. }
  28. free(phead);
  29. phead = pcur = NULL;
  30. }

最后我们将双向链表的源码附上

list.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. #include<stdbool.h>
  6. typedef int LTDataType;
  7. typedef struct ListNode ListNode;
  8. struct ListNode
  9. {
  10. LTDataType data;
  11. ListNode* next;
  12. ListNode* prev;
  13. };
  14. ListNode* LTBuyNode(LTDataType x);
  15. //为了保存接口的一致性
  16. //
  17. //初始化
  18. //void LTInit(ListNode** pphead);
  19. ListNode* LTInit();
  20. //
  21. void LTPrint(ListNode* phead);
  22. bool Empty(ListNode* phead);
  23. //插入
  24. //第一个参数传一级还是二级,要看phead指向的结点会不会改变
  25. //如果发生改变,那么pphead的改变要影响实参,传二级
  26. //如果不发生改变,pphead不会影响实参,传一级
  27. //尾插
  28. void LTPushBack(ListNode* phead, LTDataType x);
  29. //头插
  30. void LTPushFront(ListNode* phead, LTDataType x);
  31. //尾删
  32. void LTPopBack(ListNode* phead);
  33. //头删
  34. void LTPopFront(ListNode* phead);
  35. //在pos之后插入结点
  36. void LTInsert(ListNode* pos, LTDataType x);
  37. //删除指定位置的结点
  38. void LTErase(ListNode* pos);
  39. //找数据
  40. ListNode* LTFind(ListNode* phead, LTDataType x);
  41. //销毁
  42. void LTDesTroy(ListNode** pphead);
  43. void LTDesTroy2(ListNode* phead);//传一级,需要手动将plist置为NULL

 List.c

  1. #include"List.h"
  2. ListNode* LTBuyNode(LTDataType x)
  3. {
  4. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  5. if (newnode == NULL)
  6. {
  7. perror("malloc fail!");
  8. exit(1);
  9. }
  10. newnode->data = x;
  11. newnode->next = newnode->prev = newnode;
  12. return newnode;
  13. }
  14. //初始化
  15. //void LTInit(ListNode** pphead)
  16. //{
  17. // //创建一个头结点即“哨兵位”
  18. // *pphead = LTBuyNode(-1);
  19. //}
  20. ListNode* LTInit()
  21. {
  22. ListNode* phead = LTBuyNode(-1);
  23. return phead;
  24. }
  25. //打印
  26. void LTPrint(ListNode* phead)
  27. {
  28. ListNode* pcur = phead->next;
  29. while (pcur != phead)
  30. {
  31. printf("%d->", pcur->data);
  32. pcur = pcur->next;
  33. }
  34. printf("\n");
  35. }
  36. bool Empty(ListNode* phead)
  37. {
  38. assert(phead);
  39. return phead->next == phead;
  40. }
  41. //尾插
  42. void LTPushBack(ListNode* phead, LTDataType x)
  43. {
  44. assert(phead);
  45. //创建需要插入的结点
  46. ListNode* newnode = LTBuyNode(x);
  47. newnode->next = phead;
  48. newnode->prev = phead->prev;
  49. phead->prev->next = newnode;
  50. phead->prev = newnode;
  51. }
  52. //头插
  53. void LTPushFront(ListNode* phead, LTDataType x)
  54. {
  55. assert(phead);
  56. ListNode* newnode = LTBuyNode(x);
  57. newnode->next = phead->next;
  58. newnode->prev = phead;
  59. phead->next->prev = newnode;
  60. phead->next = newnode;
  61. }
  62. //尾删
  63. void LTPopBack(ListNode* phead)
  64. {
  65. assert(phead);
  66. assert(!Empty(phead));
  67. ListNode* del = phead->prev;
  68. ListNode* prev = del->prev;
  69. prev->next = phead;
  70. phead->prev = prev;
  71. free(del);
  72. del = NULL;
  73. }
  74. //头删
  75. void LTPopFront(ListNode* phead)
  76. {
  77. assert(phead);
  78. assert(!Empty(phead));
  79. ListNode* del = phead->next;
  80. del->next->prev = phead;
  81. phead->next = del->next;
  82. free(del);
  83. del = NULL;
  84. }
  85. //找相同数据
  86. ListNode* LTFind(ListNode* phead, LTDataType x)
  87. {
  88. assert(phead);
  89. ListNode* pcur = phead->next;
  90. while (pcur != phead)
  91. {
  92. if (pcur->data == x)
  93. {
  94. return pcur;
  95. }
  96. pcur = pcur->next;
  97. }
  98. return NULL;
  99. }
  100. //在pos之后插入结点
  101. void LTInsert(ListNode* pos, LTDataType x)
  102. {
  103. assert(pos);
  104. ListNode* newnode = LTBuyNode(x);
  105. newnode->next = pos->next;
  106. newnode->prev = pos;
  107. pos->next->prev = newnode;
  108. pos->next = newnode;
  109. }
  110. //删除指定位置节点
  111. void LTErase(ListNode* pos)
  112. {
  113. assert(pos);
  114. // pos->prev pos pos->next
  115. pos->prev->next = pos->next;
  116. pos->next->prev = pos->prev;
  117. free(pos);
  118. pos = NULL;
  119. }
  120. //销毁
  121. void LTDesTroy(ListNode** pphead)
  122. {
  123. assert(pphead && *pphead);
  124. ListNode* pcur = (*pphead)->next;
  125. while (pcur != *pphead)
  126. {
  127. ListNode* next = pcur->next;
  128. free(pcur);
  129. pcur = next;
  130. }
  131. //销毁哨兵位结点
  132. free(*pphead);
  133. *pphead = NULL;
  134. pcur = NULL;
  135. }
  136. void LTDesTroy2(ListNode* phead)
  137. {
  138. assert(phead);
  139. ListNode* pcur = phead->next;
  140. while (pcur != phead)
  141. {
  142. ListNode* next = pcur->next;
  143. free(pcur);
  144. pcur = next;
  145. }
  146. free(phead);
  147. phead = pcur = NULL;
  148. }

test.c 

  1. #include"List.h"
  2. void test()
  3. {
  4. 创建双向链表变量
  5. //ListNode* plist = NULL;
  6. //LTInit(&plist);
  7. ListNode* plist = LTInit();
  8. LTPushBack(plist, 1);
  9. LTPushBack(plist, 2);
  10. LTPushBack(plist, 3);
  11. LTPushBack(plist, 4);
  12. LTPrint(plist);
  13. ListNode* pos = LTFind(plist, 3);
  14. LTInsert(pos, 10);
  15. LTPrint(plist);
  16. pos = LTFind(plist, 3);
  17. LTErase(pos);
  18. LTPrint(plist);
  19. /*LTPopFront(plist);
  20. LTPrint(plist);
  21. LTPopFront(plist);
  22. LTPrint(plist);
  23. LTPopFront(plist);
  24. LTPrint(plist);
  25. LTPopFront(plist);
  26. LTPrint(plist);*/
  27. /*LTPushFront(plist, 1);
  28. LTPrint(plist);
  29. LTPushFront(plist, 2);
  30. LTPrint(plist);
  31. LTPushFront(plist, 3);
  32. LTPrint(plist);
  33. LTPushFront(plist, 4);
  34. LTPrint(plist);*/
  35. /*LTPopBack(plist);
  36. LTPrint(plist);
  37. LTPopBack(plist);
  38. LTPrint(plist);
  39. LTPopBack(plist);
  40. LTPrint(plist);
  41. LTPopBack(plist);
  42. LTPrint(plist);
  43. LTPopBack(plist);
  44. LTPrint(plist);*/
  45. /*if (pos == NULL)
  46. {
  47. printf("没有找到\n");
  48. }
  49. else
  50. {
  51. printf("找到了\n");
  52. }*/
  53. LTDesTroy(&plist);
  54. //LTDesTroy2(plist);
  55. //plist = NULL;
  56. }
  57. int main()
  58. {
  59. test();
  60. return 0;
  61. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/904702
推荐阅读
相关标签
  

闽ICP备14008679号