当前位置:   article > 正文

【数据结构】带头双向循环链表的实现及链表顺序表的区别

【数据结构】带头双向循环链表的实现及链表顺序表的区别

目录

一、带头双向循环链表接口实现

连接关系:

创建哨兵位(表头):

头插——头删:

尾插——尾删:

查找——打印:

指定位置pos前插入,删除pos位置:

链表销毁:

二、快速实现“链表”

三、链表和顺序表的区别:

四、整体代码:

一、带头双向循环链表接口实现

连接关系:

实现这个链表最重要的就是理清楚连接关系:这里我给出两个动图,模拟这个关系

插入:

删除:

创建哨兵位(表头):

这里我们可以用两种方法,一种是采取二级指针的方法,另一种是直接创建一个表头,然后把表头地址返回去,这里我们采用第二种:

  1. //创建新节点
  2. ListNode* CreateNewNode(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 = NULL;
  12. newnode->_prev = NULL;
  13. return newnode;
  14. }
  15. // 创建返回链表的头结点.
  16. ListNode* ListCreate()
  17. {
  18. ListNode* head = CreateNewNode(-1);
  19. head->_prev = head;
  20. head->_next = head;
  21. return head;
  22. }

头插——头删:

头插:

头插的时候,我们保存好链表第一个有效节点的地址,然后插入,记得把连接关系连接清楚

头删:

头删的时候,记得保存好,第二个节点的地址,记得把表头与第二节点的连接关系连接清楚

  1. // 双向链表头插
  2. void ListPushFront(ListNode* pHead, LTDataType x)
  3. {
  4. assert(pHead);
  5. ListNode* newnode = CreateNewNode(x);
  6. ListNode* second = pHead->_next;
  7. pHead->_next = newnode;
  8. newnode->_prev = pHead;
  9. newnode->_next = second;
  10. second->_prev = newnode;
  11. }
  12. // 双向链表头删
  13. void ListPopFront(ListNode* pHead)
  14. {
  15. assert(pHead);
  16. assert(pHead->_next != pHead);
  17. ListNode* first = pHead->_next;
  18. ListNode* second = first->_next;
  19. pHead->_next = second;
  20. second->_prev = pHead;
  21. free(first);
  22. first = NULL;
  23. }

尾插——尾删:

尾插:

保存好当前尾节点的地址,注意搞清楚头节点,尾节点,新节点的连接关系

尾删:

保存好最后两个节点的位置,注意连接清楚头节点和倒数第二个节点

  1. // 双向链表尾插
  2. void ListPushBack(ListNode* pHead, LTDataType x)
  3. {
  4. assert(pHead);
  5. ListNode* newnode= CreateNewNode(x);
  6. ListNode* tail = pHead->_prev;
  7. newnode->_next = pHead;
  8. newnode->_prev = tail;
  9. tail->_next = newnode;
  10. pHead->_prev = newnode;
  11. }
  12. // 双向链表尾删
  13. void ListPopBack(ListNode* pHead)
  14. {
  15. assert(pHead);
  16. assert(pHead->_next != pHead);
  17. ListNode* tail = pHead->_prev;
  18. ListNode* tailPrev = tail->_prev;
  19. pHead->_prev = tailPrev;
  20. tailPrev->_next = pHead;
  21. free(tail);
  22. tail = NULL;
  23. }

查找——打印:

注意:

循环从哨兵位的下一个节点开始,直到等于哨兵位结束

  1. // 双向链表查找
  2. ListNode* ListFind(ListNode* pHead, LTDataType x)
  3. {
  4. assert(pHead);
  5. ListNode* cur = pHead->_next;
  6. while (cur != pHead)
  7. {
  8. if (cur->_data == x)
  9. return cur;
  10. cur = cur->_next;
  11. }
  12. return NULL;
  13. }
  14. // 双向链表打印
  15. void ListPrint(ListNode* pHead)
  16. {
  17. assert(pHead);
  18. ListNode* cur = pHead->_next;
  19. while (cur!=pHead)
  20. {
  21. printf("%d <-> ", cur->_data);
  22. cur = cur->_next;
  23. }
  24. printf("NULL\n");
  25. }

指定位置pos前插入,删除pos位置:

pos前插入: 

保存好pos前一个位置的地址,连接清楚就行了

删除pos位置:

保存好pos前后的位置,连接清楚就行了

  1. // 双向链表在pos的前面进行插入
  2. void ListInsert(ListNode* pos, LTDataType x)
  3. {
  4. assert(pos);
  5. ListNode* newnode = CreateNewNode(x);
  6. ListNode* prev = pos->_prev;
  7. prev->_next = newnode;
  8. newnode->_prev = prev;
  9. newnode->_next = pos;
  10. pos->_prev = newnode;
  11. }
  12. // 双向链表删除pos位置的节点
  13. void ListErase(ListNode* pos)
  14. {
  15. assert(pos);
  16. ListNode* prev = pos->_prev;
  17. ListNode* next = pos->_next;
  18. prev->_next = next;
  19. next->_prev = prev;
  20. free(pos);
  21. pos = NULL;
  22. }

链表销毁:

这一个要从哨兵位的下一个节点开始,循环直到不等于哨兵位结束:

  1. // 双向链表销毁
  2. void ListDestory(ListNode* pHead)
  3. {
  4. assert(pHead);
  5. ListNode* cur = pHead->_next;
  6. while (cur != pHead)
  7. {
  8. ListNode* next = cur->_next;
  9. free(cur);
  10. cur = next;
  11. }
  12. free(pHead);
  13. }

二、快速实现“链表”

学了带头双向循环链表之后,我们可以快速实现一个简单链表:

大多数接口其实都是不变的,但是有4个接口可以简化,那就是头插——头删——尾插——尾删

这两个接口可以复用insert接口和erase接口:这四个接口连接关系是比较麻烦的,省去了这几个麻烦的,其他的接口就会很快写出来,大大节省了时间:

  1. // 双向链表尾插
  2. void ListPushBack(ListNode* pHead, LTDataType x)
  3. {
  4. assert(pHead);
  5. //ListNode* newnode= CreateNewNode(x);
  6. //ListNode* tail = pHead->_prev;
  7. //newnode->_next = pHead;
  8. //newnode->_prev = tail;
  9. //tail->_next = newnode;
  10. //pHead->_prev = newnode;
  11. ListInsert(pHead,x);
  12. }
  13. // 双向链表尾删
  14. void ListPopBack(ListNode* pHead)
  15. {
  16. assert(pHead);
  17. //assert(pHead->_next != pHead);
  18. //ListNode* tail = pHead->_prev;
  19. //ListNode* tailPrev = tail->_prev;
  20. //pHead->_prev = tailPrev;
  21. //tailPrev->_next = pHead;
  22. //free(tail);
  23. //tail = NULL;
  24. ListErase(pHead->_prev);
  25. }
  26. // 双向链表头插
  27. void ListPushFront(ListNode* pHead, LTDataType x)
  28. {
  29. assert(pHead);
  30. //ListNode* newnode = CreateNewNode(x);
  31. //ListNode* second = pHead->_next;
  32. //pHead->_next = newnode;
  33. //newnode->_prev = pHead;
  34. //newnode->_next = second;
  35. //second->_prev = newnode;
  36. ListInsert(pHead->_next, x);
  37. }
  38. // 双向链表头删
  39. void ListPopFront(ListNode* pHead)
  40. {
  41. assert(pHead);
  42. //assert(pHead->_next != pHead);
  43. //ListNode* first = pHead->_next;
  44. //ListNode* second = first->_next;
  45. //pHead->_next = second;
  46. //second->_prev = pHead;
  47. //free(first);
  48. //first = NULL;
  49. ListErase(pHead->_next);
  50. }

三、链表和顺序表的区别:

顺序表空间连续支持下标访问使得我们在排序,访问等很多场景下都是很高效的

链表的合理利用空间使我们对空间的管理更合理

四、整体代码:

SList.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. // 带头+双向+循环链表增删查改实现
  6. typedef int LTDataType;
  7. typedef struct ListNode
  8. {
  9. LTDataType _data;
  10. struct ListNode* _next;
  11. struct ListNode* _prev;
  12. }ListNode;
  13. // 创建返回链表的头结点.
  14. ListNode* ListCreate();
  15. // 双向链表销毁
  16. void ListDestory(ListNode* pHead);
  17. // 双向链表打印
  18. void ListPrint(ListNode* pHead);
  19. // 双向链表尾插
  20. void ListPushBack(ListNode* pHead, LTDataType x);
  21. // 双向链表尾删
  22. void ListPopBack(ListNode* pHead);
  23. // 双向链表头插
  24. void ListPushFront(ListNode* pHead, LTDataType x);
  25. // 双向链表头删
  26. void ListPopFront(ListNode* pHead);
  27. // 双向链表查找
  28. ListNode* ListFind(ListNode* pHead, LTDataType x);
  29. // 双向链表在pos的前面进行插入
  30. void ListInsert(ListNode* pos, LTDataType x);
  31. // 双向链表删除pos位置的节点
  32. void ListErase(ListNode* pos);

SList.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include"List.h"
  3. //创建新节点
  4. ListNode* CreateNewNode(LTDataType x)
  5. {
  6. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  7. if (newnode == NULL)
  8. {
  9. perror("malloc fail");
  10. exit(-1);
  11. }
  12. newnode->_data = x;
  13. newnode->_next = NULL;
  14. newnode->_prev = NULL;
  15. return newnode;
  16. }
  17. // 创建返回链表的头结点.
  18. ListNode* ListCreate()
  19. {
  20. ListNode* head = CreateNewNode(-1);
  21. head->_prev = head;
  22. head->_next = head;
  23. return head;
  24. }
  25. // 双向链表销毁
  26. void ListDestory(ListNode* pHead)
  27. {
  28. assert(pHead);
  29. ListNode* cur = pHead->_next;
  30. while (cur != pHead)
  31. {
  32. ListNode* next = cur->_next;
  33. free(cur);
  34. cur = next;
  35. }
  36. free(pHead);
  37. }
  38. // 双向链表打印
  39. void ListPrint(ListNode* pHead)
  40. {
  41. assert(pHead);
  42. ListNode* cur = pHead->_next;
  43. while (cur!=pHead)
  44. {
  45. printf("%d <-> ", cur->_data);
  46. cur = cur->_next;
  47. }
  48. printf("NULL\n");
  49. }
  50. // 双向链表尾插
  51. void ListPushBack(ListNode* pHead, LTDataType x)
  52. {
  53. assert(pHead);
  54. //ListNode* newnode= CreateNewNode(x);
  55. //ListNode* tail = pHead->_prev;
  56. //newnode->_next = pHead;
  57. //newnode->_prev = tail;
  58. //tail->_next = newnode;
  59. //pHead->_prev = newnode;
  60. ListInsert(pHead,x);
  61. }
  62. // 双向链表尾删
  63. void ListPopBack(ListNode* pHead)
  64. {
  65. assert(pHead);
  66. //assert(pHead->_next != pHead);
  67. //ListNode* tail = pHead->_prev;
  68. //ListNode* tailPrev = tail->_prev;
  69. //pHead->_prev = tailPrev;
  70. //tailPrev->_next = pHead;
  71. //free(tail);
  72. //tail = NULL;
  73. ListErase(pHead->_prev);
  74. }
  75. // 双向链表头插
  76. void ListPushFront(ListNode* pHead, LTDataType x)
  77. {
  78. assert(pHead);
  79. //ListNode* newnode = CreateNewNode(x);
  80. //ListNode* second = pHead->_next;
  81. //pHead->_next = newnode;
  82. //newnode->_prev = pHead;
  83. //newnode->_next = second;
  84. //second->_prev = newnode;
  85. ListInsert(pHead->_next, x);
  86. }
  87. // 双向链表头删
  88. void ListPopFront(ListNode* pHead)
  89. {
  90. assert(pHead);
  91. //assert(pHead->_next != pHead);
  92. //ListNode* first = pHead->_next;
  93. //ListNode* second = first->_next;
  94. //pHead->_next = second;
  95. //second->_prev = pHead;
  96. //free(first);
  97. //first = NULL;
  98. ListErase(pHead->_next);
  99. }
  100. // 双向链表查找
  101. ListNode* ListFind(ListNode* pHead, LTDataType x)
  102. {
  103. assert(pHead);
  104. ListNode* cur = pHead->_next;
  105. while (cur != pHead)
  106. {
  107. if (cur->_data == x)
  108. return cur;
  109. cur = cur->_next;
  110. }
  111. return NULL;
  112. }
  113. // 双向链表在pos的前面进行插入
  114. void ListInsert(ListNode* pos, LTDataType x)
  115. {
  116. assert(pos);
  117. ListNode* newnode = CreateNewNode(x);
  118. ListNode* prev = pos->_prev;
  119. prev->_next = newnode;
  120. newnode->_prev = prev;
  121. newnode->_next = pos;
  122. pos->_prev = newnode;
  123. }
  124. // 双向链表删除pos位置的节点
  125. void ListErase(ListNode* pos)
  126. {
  127. assert(pos);
  128. ListNode* prev = pos->_prev;
  129. ListNode* next = pos->_next;
  130. prev->_next = next;
  131. next->_prev = prev;
  132. free(pos);
  133. pos = NULL;
  134. }

Test.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include"List.h"
  3. void test()
  4. {
  5. ListNode* plist = ListCreate();
  6. ListPushBack(plist,1);
  7. ListPushBack(plist,2);
  8. ListPushBack(plist,3);
  9. ListPushBack(plist,4);
  10. ListPushBack(plist,5);
  11. ListPrint(plist);
  12. ListPopBack(plist);
  13. ListPopBack(plist);
  14. ListPopBack(plist);
  15. ListPrint(plist);
  16. ListPushFront(plist,6);
  17. ListPushFront(plist,7);
  18. ListPushFront(plist,8);
  19. ListPushFront(plist,9);
  20. ListPushFront(plist,10);
  21. ListPrint(plist);
  22. ListPopFront(plist);
  23. ListPopFront(plist);
  24. ListPopFront(plist);
  25. ListPrint(plist);
  26. printf("%d\n", ListFind(plist, 6)->_data);
  27. ListPrint(plist);
  28. ListInsert(ListFind(plist,6), 99);
  29. ListInsert(ListFind(plist,7), 88);
  30. ListPrint(plist);
  31. ListErase(ListFind(plist, 99));
  32. ListErase(ListFind(plist, 88));
  33. ListPrint(plist);
  34. ListDestory(plist);
  35. plist = NULL;
  36. }
  37. int main()
  38. {
  39. test();
  40. return 0;
  41. }

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/762818
推荐阅读
相关标签
  

闽ICP备14008679号