双向链表的增删查改

时间:2024-10-12 09:52:11

目录

 结构体类型中要有三个元素:1、指向前一个节点的指针prev。2、存储的数据val。3、指向后一个节点的指针next。还有要注意的点就是:头节点head和尾结点tail的关系,head->prev=tail,tail->next=head.

1.两个typedef,做好基础工作

2.需要定义的各个函数

初始化函数的细节

申请空间函数

插入函数函数细节(默认尾插)

 删除函数细节

7.关于头插尾插,头删尾删

查找函数

销毁函数

打印函数

11.完整程序加运行


  •  结构体类型中要有三个元素:1、指向前一个节点的指针prev。2、存储的数据val。3、指向后一个节点的指针next。还有要注意的点就是:头节点head和尾结点tail的关系,head->prev=tail,tail->next=head.

若一个双向链表只有头节点head那么他的head->next=head;head->prev=head;


1.两个typedef,做好基础工作

  1. typedef int LTDataType;
  2. typedef struct ListNode
  3. {
  4. struct ListNode* next;
  5. struct ListNode* prev;
  6. LTDataType data;
  7. }LTNode;

方便以后修改数据类型和简化写代码。


2.需要定义的各个函数

  1. LTNode* LTDinit();//初始化双向链表
  2. LTNode* BuyListNode(LTDataType x);//申请新的节点
  3. void ListPrint(LTNode* phead);//打印双向链表
  4. void ListInsert(LTNode* pos, LTDataType x);//在任意位置插入
  5. void ListPushBack(LTNode* phead, LTDataType x);//尾部插入
  6. void ListPushFront(LTNode* phead, LTDataType x);//头部插入
  7. void ListErase(LTNode* pos);//删除数据
  8. void ListPopBack(LTNode* phead);//尾部删除
  9. void ListPopFront(LTNode* phead);//头部删除
  10. LTNode* ListFind(LTNode* phead, LTDataType x);//查找
  11. void ListDestory(LTNode* phead);//销毁双向链表

初始化函数的细节

  1. LTNode* LTDinit()
  2. {
  3. LTNode* guard = NULL;//创建哨兵位节点,方便尾插
  4. guard = (LTNode*)malloc(sizeof(LTNode));
  5. if (guard == NULL)
  6. {
  7. perror("malloc fail\n");
  8. exit;//开辟成功则继续,失败则退出程序
  9. }
  10. else
  11. {
  12. guard->next = guard;
  13. guard->prev = guard;
  14. }
  15. return guard;
  16. }

有一个哨兵位节点就方便后续的插入,当然在销毁双向链表的时候也需要在哨兵位上注意一点


申请空间函数

参数是数据x,返回值是新开辟空间的地址,找到地址后就可以方便插入操作

  1. LTNode* BuyListNode(LTDataType x)
  2. {
  3. LTNode* cur = NULL;
  4. cur = (LTNode*)malloc(sizeof(LTNode));//malloc之后一定要记得检查
  5. if (cur == NULL)
  6. {
  7. perror("malloc fail\n");
  8. }
  9. cur->next = NULL;
  10. cur->prev = NULL;
  11. cur->data = x;
  12. return cur;
  13. }

插入函数函数细节(默认尾插)

第一个参数是插入位置的地址,第二个参数是所需插入的数据

  1. void ListInsert(LTNode* pos, LTDataType x)
  2. {
  3. assert(pos);
  4. LTNode* new =BuyListNode(x);
  5. if (new == NULL)
  6. {
  7. perror("malloc fail");
  8. }
  9. LTNode* next = pos->next;
  10. new->next = next;
  11. new->prev = pos;
  12. pos->next = new;
  13. next->prev = new;
  14. }


 删除函数细节

需要传参pos,告诉位置,删除数据,并重新链接链表

  1. void ListErase(LTNode* pos)
  2. {
  3. assert(pos);
  4. LTNode* prev = pos->prev;//记录pos前的数据,方便在pos位置数据销毁后重新链接
  5. LTNode* next = pos->next;//记录pos后的数据,方便在pos位置数据销毁后重新链接
  6. LTNode* cur = pos;//创建一个cur新指针用来free空间
  7. prev->next = next;
  8. next->prev = prev;
  9. free(cur);
  10. }

函数的效果图如下


7.关于头插尾插,头删尾删

头插尾插就是ListInsert函数的特殊情况,在头部和尾部插入;

同理,头删尾删就是ListErase函数的特殊情况,在头部和尾部删除;

因此可以借助这两个原本已有的函数,只需要传入头尾的地址即可

  1. void ListPopBack(LTNode* phead)//尾删
  2. {
  3. assert(phead);
  4. LTNode* cur = phead->prev;
  5. ListErase(phead->prev);
  6. free(cur);
  7. cur = NULL;
  8. }
  9. void ListPopFront(LTNode* phead)//头删
  10. {
  11. assert(phead);
  12. LTNode* cur = phead;
  13. ListErase(phead);
  14. free(cur);
  15. cur = NULL;
  16. }
  1. void ListPushBack(LTNode* phead, LTDataType x)//尾插
  2. {
  3. assert(phead);
  4. ListInsert(phead->prev, x);
  5. }
  6. void ListPushFront(LTNode* phead, LTDataType x)//头插
  7. {
  8. assert(phead);
  9. ListInsert(phead, x);
  10. }

查找函数

返回值是指针,若没找到则放回一个NULL。

  1. LTNode* ListFind(LTNode* phead, LTDataType x)
  2. {
  3. assert(phead);
  4. LTNode* cur = phead->next;//将head地址赋给cur,通过cur遍历
  5. while (cur != phead)
  6. {
  7. if (cur->data == x)
  8. return cur;
  9. cur = cur->next;
  10. }//当cur==head的时候说明双向链表已经遍历完一遍了,此时还未找到,则说明所给地址不存在于此链表中
  11. return NULL;
  12. }

销毁函数

  1. void ListDestory(LTNode* phead)
  2. {
  3. assert(phead);
  4. LTNode* cur = phead->next;//同上,用cur从头遍历
  5. while (cur != phead)
  6. {
  7. LTNode* next = cur->next;//用next记住原本的下一个位置,方便在销毁后迭代
  8. free(cur);
  9. cur = next;
  10. }
  11. free(phead);//最后把phead位置的内存给释放
  12. }

打印函数

给上头结点,往后依次打印

  1. void ListPrint(LTNode* phead)
  2. {
  3. assert(phead);
  4. LTNode* cur = phead->next;
  5. while (cur!=phead)
  6. {
  7. printf("%d<->", cur->data);
  8. cur = cur->next;
  9. }
  10. printf("NULL\n");
  11. }

11.完整程序加运行

  1. LTNode* LTDinit()
  2. {
  3. LTNode* guard = NULL;
  4. guard = (LTNode*)malloc(sizeof(LTNode));
  5. if (guard == NULL)
  6. {
  7. perror("malloc fail\n");
  8. exit;
  9. }
  10. else
  11. {
  12. guard->next = guard;
  13. guard->prev = guard;
  14. }
  15. return guard;
  16. }
  17. LTNode* BuyListNode(LTDataType x)
  18. {
  19. LTNode* cur = NULL;
  20. cur = (LTNode*)malloc(sizeof(LTNode));//malloc之后一定要记得检查
  21. if (cur == NULL)
  22. {
  23. perror("malloc fail\n");
  24. }
  25. cur->next = NULL;
  26. cur->prev = NULL;
  27. cur->data = x;
  28. return cur;
  29. }
  30. void ListPrint(LTNode* phead)
  31. {
  32. assert(phead);
  33. LTNode* cur = phead->next;
  34. while (cur!=phead)
  35. {
  36. printf("%d<->", cur->data);
  37. cur = cur->next;
  38. }
  39. printf("NULL\n");
  40. }
  41. void ListPushBack(LTNode* phead, LTDataType x)
  42. {
  43. assert(phead);
  44. ListInsert(phead->prev, x);
  45. }
  46. void ListPushFront(LTNode* phead, LTDataType x)
  47. {
  48. assert(phead);
  49. ListInsert(phead, x);
  50. }
  51. void ListInsert(LTNode* pos, LTDataType x)
  52. {
  53. assert(pos);
  54. LTNode* new =BuyListNode(x);
  55. if (new == NULL)
  56. {
  57. perror("malloc fail");
  58. }
  59. LTNode* next = pos->next;
  60. new->next = next;
  61. new->prev = pos;
  62. pos->next = new;
  63. next->prev = new;
  64. }void ListErase(LTNode* pos)
  65. {
  66. assert(pos);
  67. LTNode* prev = pos->prev;
  68. LTNode* next = pos->next;
  69. LTNode* cur = pos;
  70. prev->next = next;
  71. next->prev = prev;
  72. free(cur);
  73. cur = NULL;
  74. }
  75. void ListPopBack(LTNode* phead)
  76. {
  77. assert(phead);
  78. LTNode* cur = phead->prev;
  79. ListErase(phead->prev);
  80. free(cur);
  81. cur = NULL;
  82. }
  83. void ListPopFront(LTNode* phead)
  84. {
  85. assert(phead);
  86. LTNode* cur = phead;
  87. ListErase(phead);
  88. free(cur);
  89. cur = NULL;
  90. }
  91. LTNode* ListFind(LTNode* phead, LTDataType x)
  92. {
  93. assert(phead);
  94. LTNode* cur = phead->next;
  95. while (cur != phead)
  96. {
  97. if (cur->data == x)
  98. return cur;
  99. cur = cur->next;
  100. }
  101. return NULL;
  102. }void ListDestory(LTNode* phead)
  103. {
  104. assert(phead);
  105. LTNode* cur = phead->next;
  106. while (cur != phead)
  107. {
  108. LTNode* next = cur->next;
  109. free(cur);
  110. cur = next;
  111. }
  112. free(phead);
  113. }

头文件如下:

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <>
  3. #include <>
  4. #include <>
  5. #include <>
  6. typedef int LTDataType;
  7. typedef struct ListNode
  8. {
  9. struct ListNode* next;
  10. struct ListNode* prev;
  11. LTDataType data;
  12. }LTNode;
  13. LTNode* LTDinit();//初始化双向链表
  14. LTNode* BuyListNode(LTDataType x);//申请新的节点
  15. void ListPrint(LTNode* phead);//打印双向链表
  16. void ListInsert(LTNode* pos, LTDataType x);//在任意位置插入
  17. void ListPushBack(LTNode* phead, LTDataType x);//尾部插入
  18. void ListPushFront(LTNode* phead, LTDataType x);//头部插入
  19. void ListErase(LTNode* pos);//删除数据
  20. void ListPopBack(LTNode* phead);//尾部删除
  21. void ListPopFront(LTNode* phead);//头部删除
  22. LTNode* ListFind(LTNode* phead, LTDataType x);//查找
  23. bool ListEmpty(LTNode* phead);//判断双向链表是否为空
  24. void ListDestory(LTNode* phead);//销毁双向链表