当前位置:   article > 正文

初阶数据结构之双向链表详解

初阶数据结构之双向链表详解

目录

一:双向链表的概念

1.什么是双向链表?

2.双向链表的优点

3.双向链表的结构

二:双向链表的实现

1.定义链表结点

2.初始化双向链表

3.添加结点

4.尾插

5.头插

6.打印双向链表

7.查找链表结点

8.在指定结点后插入新结点

9.删除指定链表结点

10.头删

11.尾删

12.销毁双向链表

三:代码综合

1.List.h:

2.List.c

3.test.c


开门见山,直接开始讲解。

如果内容对你有所帮助,不妨点个赞再走。(如果有错误请指出,一定改正)

                     

一:双向链表的概念

1.什么是双向链表?

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表,因此,本节的双向链表也是双向循环链表。

2.双向链表的优点

1、双向性:双链表支持在每个节点上存储前驱节点和后继节点的指针,使得在任何节点上都可以方便地找到其前驱节点和后继节点。而单链表只能通过遍历整个链表来查找特定节点的下一个节点或上一个节点,效率较低。

2、插入和删除操作更高效:由于双链表支持双向链接,因此在插入和删除操作中,双链表只需要重新调整相关的指针即可,而不需要像单链表那样需要遍历整个链表。这使得双链表的插入和删除操作更高效。

3、内存利用率更高:双向链表每个节点存储了前驱节点和后继节点的指针,因此可以更充分地利用内存空间。相比之下,单链表每个节点只存储了一个指向下一个节点的指针,内存利用率相对较低。

3.双向链表的结构

图示:

头结点head的prev的指针指向d3,d3的next指针指向head。

同时,无头单向非循环链表和带头双向循环链表也是我们平时最常用的链表结构。

接下来,我们来聊聊双向链表的实现

二:双向链表的实现

在进行链表的实现时,我们把函数的声明放在List.h中,在List.c中实现,在test.c中调用。

1.定义链表结点

在List.h中定义链表结点,data的类型设为DataType,方便以后使用其他类型的数据。

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

2.初始化双向链表

初始化链表就是为链表设置一个头结点,头结点内不存储有效数据,只在链表中充当哨兵位的作用,然后把头结点的的prev指针和next指针都指向自己

传递二级指针的原因是需要把头结点指针的指向改为指向新开辟空间的地址,因为是对地址进行操作,因此要传递二级指针。

  1. //双向链表初始化
  2. void InitList(ListNode** head)
  3. {
  4. *head = (ListNode*)malloc(sizeof(ListNode));
  5. if (*head == NULL)
  6. {
  7. perror("malloc");
  8. exit(1);
  9. }
  10. (*head)->data = 1;
  11. (*head)->prev = *head;
  12. (*head)->next = *head;
  13. }

 运行结果:

3.添加结点

添加链表结点添加链表结点其实就是把非结点结构的数据转换为结点类型的数据,在链表插入新节点时使用。

  1. //添加结点
  2. ListNode* BuyNode(DataType x)
  3. {
  4. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  5. if (newnode == NULL)
  6. {
  7. perror("malloc");
  8. return NULL;
  9. }
  10. newnode->data = x;
  11. newnode->prev = newnode;
  12. newnode->next = newnode;
  13. return newnode;
  14. }

4.尾插

代码展示:

  1. //尾插
  2. void PushBack(ListNode* head, DataType x)
  3. {
  4. ListNode* newnode = BuyNode(x);
  5. newnode->next = head;
  6. newnode->prev = head->prev;
  7. head->prev->next = newnode;
  8. head->prev = newnode;
  9. }

当传过来的x为2时,运行结果:

5.头插

代码展示:

  1. //头插
  2. void PushFront(ListNode* head, DataType x)
  3. {
  4. ListNode* newnode = BuyNode(x);
  5. newnode->next = head->next;
  6. newnode->prev = head;
  7. head->next->prev = newnode;
  8. head->next = newnode;
  9. }

6.打印双向链表

代码展示:

  1. //打印双向链表
  2. void PrintList(ListNode* head)
  3. {
  4. assert(head && head->next);//判断链表的哨兵位和下一个结点是否为空
  5. ListNode* tail = head->next;
  6. while (tail != head)
  7. {
  8. printf("%d->", tail->data);
  9. tail = tail->next;
  10. }
  11. printf("\n");
  12. }

 在上面我们已经进行过尾插和头插的操作,让我们打印出来此时链表中的内容为:

7.查找链表结点

  1. //查找结点
  2. ListNode* FindNode(ListNode* head, DataType x)
  3. {
  4. assert(head && head->next);
  5. ListNode* tail = head->next;
  6. while (tail != head)
  7. {
  8. if (tail->data == x)
  9. {
  10. return tail;
  11. }
  12. tail = tail->next;
  13. }
  14. }

8.在指定结点后插入新结点

需要先使用FindNode函数查找指定结点,再把x转换为链表类型,最后插入。

  1. //在指定结点后面插入结点
  2. void InsertNode(ListNode* head, DataType a, DataType x)
  3. {
  4. assert(head && head->next);
  5. ListNode* pos = FindNode(head,a);
  6. ListNode* newnode = BuyNode(x);
  7. newnode->next = pos->next;
  8. newnode->prev = pos;
  9. pos->next->prev = newnode;
  10. pos->next = newnode;
  11. }

假设a为3,x为6时,运行结果为:

9.删除指定链表结点

删除链表结点要先用FindNode查找结点的地址,然后再进行删除。

  1. //删除指定结点
  2. void DelNode(ListNode* head, DataType x)
  3. {
  4. assert(head && head->next);
  5. ListNode* pos = FindNode(head,x);
  6. pos->prev->next = pos->next;
  7. pos->next->prev = pos->prev;
  8. free(pos);
  9. pos = NULL;
  10. }

假设删除的数据x为3,则运行结果为:

事实证明,我们删除成功了。

10.头删

代码展示:

  1. //头删
  2. void DelFront(ListNode* head)
  3. {
  4. assert(head && head->next);
  5. ListNode* first = head->next;
  6. first->next->prev = head;
  7. head->next = first->next;
  8. free(first);
  9. first = NULL;
  10. }

运行结果:

 头删成功。

11.尾删

  1. //尾删
  2. void DelBack(ListNode* head)
  3. {
  4. assert(head && head->next);
  5. ListNode* back = head->prev;
  6. back->prev->next = head;
  7. head->prev = back->prev;
  8. }

运行结果:

此时我们的链表中已经没有任何数据了。

12.销毁双向链表

销毁双向链表我们也要传递二级指针,因为在函数操作中也会改变头结点的地址。

  1. //销毁双向链表
  2. void DelList(ListNode** head)
  3. {
  4. assert(head && *head);
  5. ListNode* phead = (*head)->next;
  6. while (phead!=*head)
  7. {
  8. ListNode* next = phead->next;创建next指针用来存储下一个结点的地址
  9. free(phead);
  10. phead = next;
  11. }
  12. free(*head);
  13. *head=NULL;
  14. }

至此,双向链表的实现全部完成。

三:代码综合

1.List.h:

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. typedef int DataType;
  6. typedef struct ListNode
  7. {
  8. DataType data;
  9. struct ListNode* prev;
  10. struct ListNode* next;
  11. }ListNode;
  12. //双向链表初始化
  13. void InitList(ListNode** head);
  14. //添加结点
  15. ListNode* BuyNode(DataType x);
  16. //尾插结点
  17. void PushBack(ListNode*head,DataType x);
  18. //头插结点
  19. void PushFront(ListNode* head, DataType x);
  20. //打印双向链表
  21. void PrintList(ListNode* head);
  22. //查找结点
  23. ListNode* FindNode(ListNode* head, DataType x);
  24. //指定结点后面插入结点
  25. void InsertNode(ListNode* head,DataType a, DataType x);
  26. //删除指定结点
  27. void DelNode(ListNode* head, DataType);
  28. //头删
  29. void DelFront(ListNode* head);
  30. //尾删
  31. void DelBack(ListNode* head);
  32. //销毁双向链表
  33. void DelList(ListNode** head);

2.List.c

  1. #include"List.h"
  2. //双向链表初始化
  3. void InitList(ListNode** head)
  4. {
  5. *head = (ListNode*)malloc(sizeof(ListNode));
  6. if (*head == NULL)
  7. {
  8. perror("malloc");
  9. exit(1);
  10. }
  11. (*head)->data = 1;
  12. (*head)->prev = *head;
  13. (*head)->next = *head;
  14. }
  15. //添加结点
  16. ListNode* BuyNode(DataType x)
  17. {
  18. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  19. if (newnode == NULL)
  20. {
  21. perror("malloc");
  22. return NULL;
  23. }
  24. newnode->data = x;
  25. newnode->prev = newnode;
  26. newnode->next = newnode;
  27. return newnode;
  28. }
  29. //尾插
  30. void PushBack(ListNode* head, DataType x)
  31. {
  32. ListNode* newnode = BuyNode(x);
  33. newnode->next = head;
  34. newnode->prev = head->prev;
  35. head->prev->next = newnode;
  36. head->prev = newnode;
  37. }
  38. //头插
  39. void PushFront(ListNode* head, DataType x)
  40. {
  41. ListNode* newnode = BuyNode(x);
  42. newnode->next = head->next;
  43. newnode->prev = head;
  44. head->next->prev = newnode;
  45. head->next = newnode;
  46. }
  47. //打印双向链表
  48. void PrintList(ListNode* head)
  49. {
  50. assert(head && head->next);
  51. ListNode* tail = head->next;
  52. while (tail != head)
  53. {
  54. printf("%d->", tail->data);
  55. tail = tail->next;
  56. }
  57. printf("\n");
  58. }
  59. //查找结点
  60. ListNode* FindNode(ListNode* head, DataType x)
  61. {
  62. assert(head && head->next);
  63. ListNode* tail = head->next;
  64. while (tail != head)
  65. {
  66. if (tail->data == x)
  67. {
  68. return tail;
  69. }
  70. tail = tail->next;
  71. }
  72. }
  73. //在指定结点后面插入结点
  74. void InsertNode(ListNode* head, DataType a, DataType x)
  75. {
  76. assert(head && head->next);
  77. ListNode* pos = FindNode(head,a);
  78. ListNode* newnode = BuyNode(x);
  79. newnode->next = pos->next;
  80. newnode->prev = pos;
  81. pos->next->prev = newnode;
  82. pos->next = newnode;
  83. }
  84. //删除指定结点
  85. void DelNode(ListNode* head, DataType x)
  86. {
  87. assert(head && head->next);
  88. ListNode* pos = FindNode(head,x);
  89. pos->prev->next = pos->next;
  90. pos->next->prev = pos->prev;
  91. free(pos);
  92. pos = NULL;
  93. }
  94. //头删
  95. void DelFront(ListNode* head)
  96. {
  97. assert(head && head->next);
  98. ListNode* first = head->next;
  99. first->next->prev = head;
  100. head->next = first->next;
  101. free(first);
  102. first = NULL;
  103. }
  104. //尾删
  105. void DelBack(ListNode* head)
  106. {
  107. assert(head && head->next);
  108. ListNode* back = head->prev;
  109. back->prev->next = head;
  110. head->prev = back->prev;
  111. }
  112. //销毁双向链表
  113. void DelList(ListNode** head)
  114. {
  115. assert(head && *head);
  116. ListNode* phead = (*head)->next;
  117. while (phead!=*head)
  118. {
  119. ListNode* next = phead->next;
  120. free(phead);
  121. phead = next;
  122. }
  123. free(*head);
  124. *head =NULL;
  125. }

3.test.c

  1. #include"List.h"
  2. int main()
  3. {
  4. ListNode* head=NULL;
  5. //初始化
  6. InitList(&head);
  7. //尾插
  8. PushBack(head, 2);
  9. printf("%d", head->next->data);
  10. //头插
  11. PushFront(head, 3);
  12. printf("%d\n", head->next->data);
  13. //打印链表
  14. PrintList(head);
  15. //查找结点
  16. ListNode* node = FindNode(head, 3);
  17. //指定结点后面插入新节点
  18. InsertNode(head, 3, 6);
  19. PrintList(head);
  20. //删除指定结点
  21. DelNode(head, 3);
  22. PrintList(head);
  23. //头删
  24. DelFront(head);
  25. PrintList(head);
  26. //尾删
  27. DelBack(head);
  28. PrintList(head);
  29. //销毁双向链表
  30. DelList(&head);
  31. PrintList(head);
  32. }

完结撒花。

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

闽ICP备14008679号