当前位置:   article > 正文

【数据结构】手撕双向链表

【数据结构】手撕双向链表

目录

前言

1. 双向链表 

带头双向循环链表的结构

2. 链表的实现

2.1 初始化

2.2 尾插

2.3 尾删

2.4 头插

2.5 头删

2.6 在pos位置之前插入

2.7 删除pos位置

3.双向链表完整源码

List.h

List.c


前言

在上一期中我们介绍了单链表,也做了一些练习题,在一些题中使用单链表会十分繁琐。因为单链表只能正着走,不能倒着走,例如:回文、逆置。本期我们将学习带头双向循环链表。

1. 双向链表 

带头双向循环链表的结构

 特点:带头双向循环链表结构最复杂,一般用在单独存储数据。结构虽然结构复杂,但是使用代码实现以后会发现结构会带来多优势,实现反而简单了。

2. 链表的实现

2.1 初始化

  1. LTNode* LTInit()
  2. {
  3. LTNode* phead = CreateLTNode(-1);
  4. phead->next = phead;
  5. phead->prev = phead;
  6. return phead;
  7. }

2.2 尾插

带哨兵位的链表尾插时不用判断是否有节点,两种情况的插入相同,而且还不用传递二级指针。

  1. void LTPushBack(LTNode* phead, LTDateType x)
  2. {
  3. assert(phead);
  4. LTNode* newnode = CreateLTNode(x);
  5. phead->prev->next = newnode;
  6. newnode->prev = phead->prev;
  7. newnode->next = phead;
  8. phead->prev = newnode;
  9. }

2.3 尾删

在尾删时我们通过 assert(phead->next != phead);  判断链表是否有节点。同时这个代码就有普遍性,不用单独考虑剩一个节点的情况。

  1. void LTPopBack(LTNode* phead)
  2. {
  3. assert(phead);
  4. LTNode* tail = phead->prev;
  5. LTNode* tailprev = tail->prev;
  6. free(tail);
  7. phead->prev = tailprev;
  8. tailprev->next = phead;
  9. }

2.4 头插

头删重要的是赋值的顺序,顺序错误会找不到后面的节点,导致内存泄漏。带哨兵位的链表不需要传递二级指针,因为改变的是结构体的变量。

  1. void LTPushFront(LTNode* phead, LTDateType x)
  2. {
  3. assert(phead);
  4. LTNode* newnode = CreateLTNode(x);
  5. newnode->next = phead->next;
  6. phead->next->prev = newnode;
  7. phead->next = newnode;
  8. newnode->prev = phead;
  9. }

2.5 头删

我们可以多定义几个指针来保存后面节点的地址,这样就不会造成节点的丢失,不用考虑赋值的顺序,会更加方便。 

  1. void LTPopFront(LTNode* phead)
  2. {
  3. assert(phead);
  4. assert(phead->next != phead);
  5. LTNode* tail = phead->next;
  6. LTNode* next = tail->next;
  7. phead->next = next;
  8. next->prev = phead;
  9. free(tail);
  10. tail = NULL;
  11. }

2.6 在pos位置之前插入

  1. void LTInsert(LTNode* pos, LTDateType x)
  2. {
  3. assert(pos);
  4. LTNode* posprev = pos->prev;
  5. LTNode* newnode = CreateLTNode(x);
  6. posprev->next = newnode;
  7. newnode->prev = posprev;
  8. newnode->next = pos;
  9. pos->prev = newnode;
  10. }

2.7 删除pos位置

  1. void LTErase(LTNode* pos)
  2. {
  3. assert(pos);
  4. LTNode* posprev = pos->prev;
  5. LTNode* posnext = pos->next;
  6. posprev->next = posnext;
  7. posnext->prev = posprev;
  8. }

3.双向链表完整源码

List.h

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<assert.h>
  4. typedef int LTDateType;
  5. typedef struct ListNode
  6. {
  7. LTDateType val;
  8. struct ListNode* next;
  9. struct ListNode* prev;
  10. }LTNode;
  11. LTNode* LTInit();
  12. void LTPrint(LTNode* phead);
  13. void LTPushBack(LTNode* phead, LTDateType x);
  14. void LTPushFront(LTNode* phead, LTDateType x);
  15. void LTPopBack(LTNode* phead);
  16. void LTPopFront(LTNode* phead);
  17. LTNode* LTFind(LTNode* phead, LTDateType x);
  18. void LTInsert(LTNode* pos, LTDateType x);
  19. void LTErase(LTNode* pos);
  20. void LTDestroy(LTNode* phead);

List.c

  1. #include"List.h"
  2. LTNode* CreateLTNode(LTDateType x)
  3. {
  4. LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
  5. if (newnode == NULL)
  6. {
  7. perror("malloc fail");
  8. exit(-1);
  9. }
  10. newnode->val = x;
  11. newnode->next = NULL;
  12. newnode->prev = NULL;
  13. }
  14. LTNode* LTInit()
  15. {
  16. LTNode* phead = CreateLTNode(-1);
  17. phead->next = phead;
  18. phead->prev = phead;
  19. return phead;
  20. }
  21. void LTPrint(LTNode* phead)
  22. {
  23. assert(phead);
  24. LTNode* cur = phead->next;
  25. printf("<=>哨兵位<=>");
  26. while (cur != phead)
  27. {
  28. printf("%d<=>", cur->val);
  29. cur = cur->next;
  30. }
  31. printf("\n");
  32. }
  33. void LTPushBack(LTNode* phead, LTDateType x)
  34. {
  35. assert(phead);
  36. LTNode* newnode = CreateLTNode(x);
  37. phead->prev->next = newnode;
  38. newnode->prev = phead->prev;
  39. newnode->next = phead;
  40. phead->prev = newnode;
  41. }
  42. void LTPushFront(LTNode* phead, LTDateType x)
  43. {
  44. assert(phead);
  45. LTNode* newnode = CreateLTNode(x);
  46. newnode->next = phead->next;
  47. phead->next->prev = newnode;
  48. phead->next = newnode;
  49. newnode->prev = phead;
  50. }
  51. void LTPopBack(LTNode* phead)
  52. {
  53. assert(phead);
  54. LTNode* tail = phead->prev;
  55. LTNode* tailprev = tail->prev;
  56. free(tail);
  57. phead->prev = tailprev;
  58. tailprev->next = phead;
  59. }
  60. void LTPopFront(LTNode* phead)
  61. {
  62. assert(phead);
  63. assert(phead->next != phead);
  64. LTNode* tail = phead->next;
  65. LTNode* next = tail->next;
  66. phead->next = next;
  67. next->prev = phead;
  68. free(tail);
  69. tail = NULL;
  70. }
  71. LTNode* LTFind(LTNode* phead, LTDateType x)
  72. {
  73. assert(phead);
  74. LTNode* cur = phead->next;
  75. while (cur != phead)
  76. {
  77. if (cur->val == x)
  78. {
  79. return cur;
  80. }
  81. cur = cur->next;
  82. }
  83. return NULL;
  84. }
  85. void LTInsert(LTNode* pos, LTDateType x)
  86. {
  87. assert(pos);
  88. LTNode* posprev = pos->prev;
  89. LTNode* newnode = CreateLTNode(x);
  90. posprev->next = newnode;
  91. newnode->prev = posprev;
  92. newnode->next = pos;
  93. pos->prev = newnode;
  94. }
  95. void LTErase(LTNode* pos)
  96. {
  97. assert(pos);
  98. LTNode* posprev = pos->prev;
  99. LTNode* posnext = pos->next;
  100. posprev->next = posnext;
  101. posnext->prev = posprev;
  102. }
  103. void LTDestroy(LTNode* phead)
  104. {
  105. assert(phead);
  106. LTNode* cur = phead->next;
  107. while (cur != phead)
  108. {
  109. LTNode* next = cur->next;
  110. free(cur);
  111. cur = next;
  112. }
  113. free(phead);
  114. phead = NULL;
  115. }

通过上面链表的实现,我们已经感受到了带头双向循环链表的方便和简单,它不需要去考虑链表是否有元素,还可以找到前一个元素,在我们使用中提供很大的便利。

本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者三连支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。 

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

闽ICP备14008679号