当前位置:   article > 正文

实现双向链表的增删查改

实现双向链表的增删查改

List.h

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

List.c

  1. #include"List.h"
  2. LTNode* LTBuyNode(LTDataType x)
  3. {
  4. LTNode* node = (LTNode*)malloc(sizeof(LTNode));
  5. if (node == NULL)
  6. {
  7. perror("malloc fail");
  8. exit(1);
  9. }
  10. node->data = x;
  11. node->next = node->prev = node;
  12. return node;
  13. }
  14. //void LTInit(LTNode** pphead)
  15. //{
  16. // *pphead = LTBuyNode(-1);
  17. //}
  18. LTNode* LTInit()
  19. {
  20. LTNode* phead = LTBuyNode(-1);
  21. return phead;
  22. }
  23. void LTPushBack(LTNode* phead, LTDataType x)
  24. {
  25. assert(phead);
  26. LTNode* newnode = LTBuyNode(x);
  27. newnode->prev = phead->prev;
  28. newnode->next = phead;
  29. phead->prev->next = newnode;
  30. phead->prev = newnode;
  31. }
  32. void LTPushFront(LTNode* phead, LTDataType x)
  33. {
  34. assert(phead);
  35. LTNode* newnode = LTBuyNode(x);
  36. newnode->next = phead->next;
  37. newnode->prev = phead;
  38. phead->next->prev = newnode;
  39. phead->next = newnode;
  40. }
  41. void LTPrint(LTNode* phead)
  42. {
  43. LTNode* pcur = phead->next;
  44. while (pcur != phead)
  45. {
  46. printf("%d->", pcur->data);
  47. pcur = pcur->next;
  48. }
  49. printf("\n");
  50. }
  51. void LTPopBack(LTNode* phead, LTDataType x)
  52. {
  53. //链表必须有效且链表不能为空(只有一个哨兵位)
  54. assert(phead&&phead->next!=phead);
  55. LTNode* del = phead->prev;
  56. del->prev->next = phead;
  57. phead->prev = del->prev;
  58. free(del);
  59. del = NULL;
  60. }
  61. void LTPopFront(LTNode* phead, LTDataType x)
  62. {
  63. assert(phead && phead->next != phead);
  64. LTNode* del = phead->next;
  65. phead->next = del->next;
  66. del->next->prev = phead;
  67. free(del);
  68. del = NULL;
  69. }
  70. LTNode* LTFind(LTNode* phead, LTDataType x)
  71. {
  72. LTNode* pcur = phead->next;
  73. while (pcur != phead)
  74. {
  75. if (pcur->data == x)
  76. {
  77. return pcur;
  78. }
  79. pcur = pcur->next;
  80. }
  81. return NULL;
  82. }
  83. void LTInsert(LTNode* pos, LTDataType x)
  84. {
  85. assert(pos);
  86. LTNode* newnode = LTBuyNode(x);
  87. newnode->next = pos->next;
  88. newnode->prev = pos;
  89. pos->next->prev = newnode;
  90. pos->next = newnode;
  91. }
  92. void LTErase(LTNode* pos)
  93. {
  94. assert(pos);
  95. pos->next->prev = pos->prev;
  96. pos->prev->next = pos->next;
  97. free(pos);
  98. pos = NULL;
  99. }
  100. void LTDestTroy(LTNode* phead)
  101. {
  102. assert(phead);
  103. LTNode* pcur = phead->next;
  104. while (pcur != phead)
  105. {
  106. LTNode* next = pcur->next;
  107. free(pcur);
  108. pcur = next;
  109. }
  110. //此时pcur指向phead,而phead还没被销毁
  111. free(phead);
  112. phead = NULL;
  113. }

test.c

  1. #include"List.h"
  2. void ListTest01()
  3. {
  4. //LTNode* plist = NULL;
  5. //LTInit(&plist);
  6. LTNode* plist = LTInit();
  7. LTPushBack(plist, 1);
  8. LTPrint(plist);
  9. LTPushBack(plist, 2);
  10. LTPrint(plist);
  11. LTPushBack(plist, 3);
  12. LTPrint(plist);
  13. LTNode* find = LTFind(plist, 3);
  14. if (find == NULL)
  15. {
  16. printf("找不到\n");
  17. }
  18. else
  19. printf("找到了\n");
  20. LTInsert(find, 66);
  21. LTPrint(plist);
  22. LTPushFront(plist, 4);
  23. LTPrint(plist);
  24. LTErase(find);
  25. find = NULL;
  26. LTPrint(plist);
  27. LTDestTroy(plist);
  28. plist = NULL;
  29. }
  30. int main()
  31. {
  32. ListTest01();
  33. return 0;
  34. }

        双向链表是一个带头链表,带头结点又俗称哨兵结点,是用来标记指针位置的作用的。

        这里我们给哨兵结点赋予一个标记值-1,当然也可以用其他值代替,这里只起到一个标记作用。实际上我们访问链表是从phead->next也就是pcur结点开始访问。

        创建一个结点,我们就得申请一个结点的内存空间:

        

        返回创建的node值,并在以后头插尾插的时候创建一个newnode结点来接受node结点的内存空间,这里我们以尾插为例:

        

这里插入一个新数据时,我们改变指针的指向就能实现尾插了,并注意这里的newnode与phead的指针指向的顺序不能改变,否则phead的next会被修改,导致找不到phead的next指针。

        在这里虽然有些函数传入了形参,但用的是一级指针,无法改变实参,我们需要在test函数中额外来释放节点。

        函数都设置为一级指针,是因为更方便我们来观察函数,不然一会一级一行二级指针,是真的很会让人抓狂的。

        在这一块我们设置一个查询节点的函数,类型为LTNode*,返回一个结点,即->data==3的那个结点,并返回。

        在后续的插入和消除一个值的时候,传入该函数的返回值find。

        

        注意这里Find函数while循环中的条件,即从pcur也就是phead的笑一个结点开始扫描,由于循环的原因,当扫到带头结点即结束扫描,从而访问到了整个链表.

而这块代码当删除头或尾结点时,为什么不可以直接调用PopBack或PopFront函数呢:

是因为该函数没有传入plist指针,无法访问到plist数组。

这里的Pop函数的条件十分重要,不能只剩下一个带头结点,因为链表必须要有效.

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

闽ICP备14008679号