当前位置:   article > 正文

【零基础学数据结构】双向链表

【零基础学数据结构】双向链表

1.双向链表的概念

1.1头节点 

 1.2带头双向循环链表

注意: 哨兵位创建后,首尾连接自己

1.3双链表的初始化

  1. // 创建节点
  2. ListNode* ListBuyNode(ListDatatype x)
  3. {
  4. ListNode* node = (ListNode*)malloc(sizeof(ListNode));
  5. if (node == NULL)
  6. {
  7. perror("malloc err!");
  8. exit(1);
  9. }
  10. node->data = x;
  11. node->next = node->prev = node; //哨兵位创建后,首尾连接自己
  12. return node;
  13. }
  14. // 双向链表的初始化
  15. void ListInit(ListNode** pphead)
  16. {
  17. // 给双链表创建一个哨兵位
  18. *pphead = ListBuyNode(-1);
  19. }

相当于给头节点也就是哨兵位附上一个无效值,我们在后续的操作中不可以访问哨兵位,它的作用只是一个向导。 

2.双向链表的打印 

  1. // 双向链表的打印
  2. void ListPrint(ListNode* phead)
  3. {
  4. // 遍历链表
  5. ListNode* pcur = phead->next;
  6. while (pcur != phead)
  7. {
  8. // 打印
  9. printf("%d->", pcur->data);
  10. pcur = pcur->next;
  11. }
  12. printf("\n");
  13. }

每次打印需要检查所传的值避开头节点, 我们第一个打印的值要从头节点的下一个节点开始,知道遍历链表走到头节点结束。

 3.双向链表的尾插 

  1. // 双向链表的尾插
  2. void ListPushBack(ListNode* phead, ListDatatype x)
  3. {
  4. // 创建一个存放数据的新节点
  5. ListNode* newnode = ListBuyNode(x);
  6. // 进行尾插
  7. // 新节点连接
  8. newnode->prev = phead->prev;
  9. newnode->next = phead;
  10. // 改变原来链表的连接
  11. phead->prev->next = newnode;
  12. phead->prev = newnode;
  13. }

尾插我们需要注意的是先要创建一个新的节点储存我要插入的值,在链表连接的时候首先把新创建节点的首尾相连,这样可以避免因为改动了原链表的指向而导致的问题!

如:我们先用(phead->prev)节点的*next指向了新节点(newnode), 那么原本d3(phead->prev)的指向就会被覆盖,后续难以找到。

4.双向链表的头插 

  1. void ListPushFront(ListNode* phead, ListDatatype x)
  2. {
  3. // 创建一个存放数据的新节点
  4. ListNode* newnode = ListBuyNode(x);
  5. // 进行头插
  6. // 新节点连接
  7. newnode->prev = phead;
  8. newnode->next = phead->next;
  9. // 改变原来链表的连接
  10. phead->next->prev = newnode;
  11. phead->next = newnode;
  12. }

和尾插的操作差不多,要注意先新后原——先连接新链表,后更改原来的链表。 

5.双向链表的尾删 

  1. void ListPopBack(ListNode* phead)
  2. {
  3. assert(phead && phead->next != phead);
  4. // 进行尾删除
  5. ListNode* del = phead->prev;// 这边创建del方便理解,不然指向过多太乱
  6. del->prev->next = del->next;
  7. del->next->prev = del->prev;
  8. // 释放空间
  9. free(del);
  10. del = NULL;
  11. }

6.双向链表的头删 

  1. void ListPopFront(ListNode* phead)
  2. {
  3. assert(phead && phead->next != phead);
  4. // 进行头删除
  5. ListNode* del = phead->next;// 这边创建del方便理解,不然指向过多太乱
  6. del->next->prev = phead;
  7. phead->next = del->next;
  8. // 释放空间
  9. free(del);
  10. del = NULL;
  11. }

7.双向链表的查找  

  1. ListNode* ListFind(ListNode* phead, ListDatatype x)
  2. {
  3. // 遍历双向链表
  4. ListNode* pcur = phead->next;
  5. while (pcur != phead)
  6. {
  7. // 打印
  8. if (pcur->data == x)
  9. {
  10. return pcur;//如果找到返回节点
  11. }
  12. pcur = pcur->next;
  13. }
  14. // 没有找到
  15. return NULL;
  16. }

 8.双向链表在pos位置之后插入 

  1. void ListInsret(ListNode* pos, ListDatatype x)
  2. {
  3. assert(pos);
  4. // 创建一个存放数据的新节点
  5. ListNode* newnode = ListBuyNode(x);
  6. // 插入
  7. // 新节点连接
  8. newnode->prev = pos;
  9. newnode->next = pos->next;
  10. // 原链表连接
  11. pos->next->prev = newnode;
  12. pos->next = newnode;
  13. }

9.双向链表删除pos节点 

  1. void ListErase(ListNode* pos)
  2. {
  3. //pos理论上来说不能为phead,但是没有参数phead,无法增加校验
  4. assert(pos);
  5. pos->prev->next = pos->next;
  6. pos->next->prev = pos->prev;
  7. free(pos);
  8. pos = NULL;
  9. }

 10.双向链表的销毁

  1. void ListDesTroy(ListNode* phead)
  2. {
  3. assert(phead);
  4. // 遍历删除
  5. ListNode* pcur = phead->next;
  6. while (pcur != phead)
  7. {
  8. ListNode* next = pcur->next;
  9. free(pcur);
  10. pcur = next;
  11. }
  12. //此时pcur指向phead,而phead还没有被销毁
  13. free(phead);
  14. phead = NULL;
  15. printf("销毁成功!");
  16. }

销毁链表需要从头一个一个往下删除释放空间,我们创建pcur指针指向头节点,而next指针指向下一个节点,next指针的作用是防止我们删除当前节点时找不到下一个节点了。循环遍历删除,直到最后删除头节点(哨兵位)。 

 测试文件:用于检验代码执行的有效性

  1. #include "List.h"
  2. void ListNodetext()
  3. {
  4. ListNode* plist;
  5. ListInit(&plist);
  6. // 测试尾插
  7. ListPushBack(plist, 1);
  8. /*ListPrint(plist);*/
  9. ListPushBack(plist, 2);
  10. /*ListPrint(plist);*/
  11. ListPushBack(plist, 3);
  12. ListPrint(plist); // 1->2->3->
  13. // 测试头插
  14. //ListPushFront(plist, 6);
  15. //ListPrint(plist);
  16. //ListPushFront(plist, 7);
  17. //ListPrint(plist);
  18. //ListPushFront(plist, 8);
  19. //ListPrint(plist);
  20. // 测试尾删
  21. //ListPopBack(plist);
  22. //ListPrint(plist);
  23. //ListPopBack(plist);
  24. //ListPrint(plist);
  25. //ListPopBack(plist);
  26. //ListPrint(plist);
  27. // 测试头删
  28. //ListPopFront(plist);
  29. //ListPrint(plist);
  30. //ListPopFront(plist);
  31. //ListPrint(plist);
  32. //ListPopFront(plist);
  33. //ListPrint(plist);
  34. // 测试查找
  35. //ListNode* find = ListFind(plist, 30);
  36. //if (find == NULL)
  37. //{
  38. // printf("没有找到!");
  39. //}
  40. //else
  41. //{
  42. // printf("找到了!");
  43. //}
  44. // 测试pos位置之后插入
  45. //ListNode* find = ListFind(plist, 2);
  46. //ListInsret(find, 5);
  47. //ListPrint(plist);
  48. // 测试删除pos节点
  49. //ListNode* find = ListFind(plist, 3);
  50. //ListErase(find);
  51. //find = NULL;
  52. //ListPrint(plist);
  53. ListDesTroy(plist);
  54. plist = NULL;
  55. }
  56. int main()
  57. {
  58. ListNodetext();
  59. return 0;
  60. }

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

闽ICP备14008679号