• 实现双向链表的增删查改


    List.h

    1. #pragma once
    2. #include
    3. #include
    4. #include
    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函数的条件十分重要,不能只剩下一个带头结点,因为链表必须要有效.

  • 相关阅读:
    11.17 - 每日一题 - 408
    十七、乐观锁和悲观锁(干货版)
    Linux top 命令详解
    《自动驾驶感知算法实战专栏(源代码)》专栏概述 | 实战教程,开放源码
    HTTP、TCP、SOCKET三者之间区别和原理
    linux环境安装SVN,以及常用的SVN操作
    NVIDIA Jetson TX2 解决奥比中光 Astra pro相机的ros 打不开深度信息/camera/depth/image
    JS判断字符串是否为json字符串,读取JSON数据 | js获取对象的方法 | 元素添加 class 属性
    GPTCache:革新大模型缓存,降低成本,提升效率
    【软件工程】【23.04】p1
  • 原文地址:https://blog.csdn.net/weixin_73863912/article/details/137744459