• 双向链表(Double Linked List)


    一、简介

            虽然单向链表能够100%解决逻辑关系为“一对一”数据的存储问题,但在解决那些需要大量查找前趋节点的问题是,单向链表无疑是不能用了,因为单向链表适合“从前往后”查找,并不适合“从后往前”查找。

            如果要提高链表的查找效率,那双向链表(双链表)无疑是首选。

            双向链表字面上的意思是“双向”的链表,如图1所示。

    图1 - 双向链表示意图

            双向指各个节点之间的逻辑关系是双向的,该链表通常只有一个头节点。

            从图1还可以看出,双向链表中每个节点包括一下3个部分,分别是指针域(用于指向当前节点的直接前驱节点)、数据域(用于存储数据元素)和指针域(用于指向当前节点的后继节点)。

    二、创建

    1、声明

    1. typedef struct line{
    2. struct line *prior;//指向直接前趋
    3. int data;
    4. struct line *next;//指向直接后继
    5. }line;

    2、创建

    1. line* initLine(line *head){
    2. head=(line*)malloc(sizeof(line));//创建链表第一个结点(首元结点)
    3. head->prior=NULL;
    4. head->next=NULL;
    5. head->data=1;
    6. line *list=head;
    7. for(int i=2; i<=3; i++)
    8. {
    9. //创建并初始化一个新结点
    10. line *body=(line*)malloc(sizeof(line));
    11. body->prior=NULL;
    12. body->next=NULL;
    13. body->data=i;
    14. list->next=body;//直接前趋结点的next指针指向新结点
    15. body->prior=list;//新结点指向直接前趋结点
    16. list=list->next;
    17. }
    18. return head;
    19. }

    三、基本操作

    1、添加节点

            添加节点可以分为三种,分别是:添加至表头添加至链表的中间位置添加至链表尾

    添加至表头

            将新元素添加到表头,只需要将其与表头元素建立双层逻辑关系即可。

            假设定义新元素节点为tmp,表头节点为head,则只需要执行下面两个步骤和即可:

    1. tmp的next变成head,head的prior编程tmp;
    2. 将head移至tmp,重新指向新的表头。

            比如将元素7天添加到双向链表的表头,则实现过程如图2所示。

    图2 - 添加元素至双向链表的表头

    添加至链表的中间位置

            添加至表的中间位置主要分为两个步骤:

    1. 新节点先与其后继节点建立双层逻辑关系;
    2. 新节点的前驱与之建立双层逻辑关系。

             此过程如图3所示。

    图3 - 双向链表中间位置添加数据元素示意图

     添加至表尾

            与添加至表头很相似,其过程如下:

    1. 找到双向链表的最后一个节点;
    2. 让新节点与其进行双层逻辑关系建立。

            此过程如图4所示。 

    图4 - 双向链表尾部添加元素示意图

    代码

    经过上述内容,我们可以试着编写代码了。

    1. line *insertLine(line *head,int data,int add){
    2. //新建数据域为data的结点
    3. line *temp=(line*)malloc(sizeof(line));
    4. temp->data=data;
    5. temp->prior=NULL;
    6. temp->next=NULL;
    7. //插入到链表头,要特殊考虑
    8. if(add==1)
    9. {
    10. temp->next=head;
    11. head->prior=temp;
    12. head=temp;
    13. }
    14. else
    15. {
    16. line *body=head;
    17. //找到要插入位置的前一个结点
    18. for(int i=1; i-1; i++)
    19. {
    20. body=body->next;
    21. }
    22. //判断条件为真,说明插入位置为链表尾
    23. if(body->next==NULL)
    24. {
    25. body->next=temp;
    26. temp->prior=body;
    27. }
    28. else
    29. {
    30. body->next->prior=temp;
    31. temp->next=body->next;
    32. body->next=temp;
    33. temp->prior=body;
    34. }
    35. }
    36. return head;
    37. }

    2、删除节点

            双向链表删除节点时,只需要遍历到要删除的节点,然后将其删除即可。

            例如,从删除2的过程如图5所示。

    图5 - 双向链表删除元素的操作示意图

    代码

    1. //删除结点的函数,data为要删除结点的数据域的值
    2. line *delLine(line *head,int data)
    3. {
    4. line *temp=head;
    5. //遍历链表
    6. while(temp)
    7. {
    8. //判断当前结点中数据域和data是否相等,若相等,摘除该结点
    9. if (temp->data==data)
    10. {
    11. temp->prior->next=temp->next;
    12. temp->next->prior=temp->prior;
    13. free(temp);
    14. return head;
    15. }
    16. temp=temp->next;
    17. }
    18. printf("链表中无该数据元素");
    19. return head;
    20. }

    3、查找节点

            依次遍历表中数据,直到找到为止。

    代码

    1. //head为原双链表,elem表示被查找元素
    2. int selectElem(line * head,int elem){
    3. //新建一个指针t,初始化为头指针 head
    4. line * t=head;
    5. int i=1;
    6. while(t)
    7. {
    8. if(t->data==elem)
    9. {
    10. return i;
    11. }
    12. i++;
    13. t=t->next;
    14. }
    15. //程序执行至此处,表示查找失败
    16. return -1;
    17. }

    4、更改节点

            在查找的基础上完成。过程是通过遍历找到的节点,直接将数据域修改即可。

    代码

    1. //更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值
    2. line *amendElem(line *p,int add,int newElem){
    3. line *temp=p;
    4. //遍历到被删除结点
    5. for (int i=1; i
    6. {
    7. temp=temp->next;
    8. }
    9. temp->data=newElem;
    10. return p;
    11. }

    四、完整代码

            给出的所有代码的整合代码:

    1. #include
    2. typedef struct line{
    3. struct line *prior;
    4. int data;
    5. struct line *next;
    6. }line;
    7. //双链表的创建
    8. line* initLine(line * head);
    9. //双链表插入元素,add表示插入位置
    10. line * insertLine(line * head,int data,int add);
    11. //双链表删除指定元素
    12. line * delLine(line * head,int data);
    13. //双链表中查找指定元素
    14. int selectElem(line * head,int elem);
    15. //双链表中更改指定位置节点中存储的数据,add表示更改位置
    16. line *amendElem(line * p,int add,int newElem);
    17. //输出双链表的实现函数
    18. void display(line * head);
    19. int main(){
    20. line *head=NULL;
    21. //创建双链表
    22. head=initLine(head);
    23. display(head);
    24. //在表中第 3 的位置插入元素 7
    25. head=insertLine(head,7,3);
    26. display(head);
    27. //表中删除元素 2
    28. head=delLine(head,2);
    29. display(head);
    30. printf("元素 3 的位置是:%d\n",selectElem(head,3));
    31. //表中第 3 个节点中的数据改为存储 6
    32. head=amendElem(head,3,6);
    33. display(head);
    34. return 0;
    35. }
    36. line* initLine(line * head){
    37. head=(line*)malloc(sizeof(line));
    38. head->prior=NULL;
    39. head->next=NULL;
    40. head->data=1;
    41. line *list=head;
    42. for(int i=2; i<=5; i++)
    43. {
    44. line*body=(line*)malloc(sizeof(line));
    45. body->prior=NULL;
    46. body->next=NULL;
    47. body->data=i;
    48. list->next=body;
    49. body->prior=list;
    50. list=list->next;
    51. }
    52. return head;
    53. }
    54. line *insertLine(line *head,int data,int add){
    55. //新建数据域为data的结点
    56. line *temp=(line*)malloc(sizeof(line));
    57. temp->data=data;
    58. temp->prior=NULL;
    59. temp->next=NULL;
    60. //插入到链表头,要特殊考虑
    61. if(add==1)
    62. {
    63. temp->next=head;
    64. head->prior=temp;
    65. head=temp;
    66. }
    67. else
    68. {
    69. line * body=head;
    70. //找到要插入位置的前一个结点
    71. for(int i=1; i-1; i++)
    72. {
    73. body=body->next;
    74. }
    75. //判断条件为真,说明插入位置为链表尾
    76. if(body->next==NULL)
    77. {
    78. body->next=temp;
    79. temp->prior=body;
    80. }
    81. else
    82. {
    83. body->next->prior=temp;
    84. temp->next=body->next;
    85. body->next=temp;
    86. temp->prior=body;
    87. }
    88. }
    89. return head;
    90. }
    91. line *delLine(line *head,int data)
    92. {
    93. line *temp=head;
    94. //遍历链表
    95. while(temp)
    96. {
    97. //判断当前结点中数据域和data是否相等,若相等,摘除该结点
    98. if(temp->data==data)
    99. {
    100. temp->prior->next=temp->next;
    101. temp->next->prior=temp->prior;
    102. free(temp);
    103. return head;
    104. }
    105. temp=temp->next;
    106. }
    107. printf("链表中无该数据元素");
    108. return head;
    109. }
    110. //head为原双链表,elem表示被查找元素
    111. int selectElem(line *head,int elem){
    112. //新建一个指针t,初始化为头指针 head
    113. line *t=head;
    114. int i=1;
    115. while(t)
    116. {
    117. if(t->data==elem)
    118. {
    119. return i;
    120. }
    121. i++;
    122. t=t->next;
    123. }
    124. //程序执行至此处,表示查找失败
    125. return -1;
    126. }
    127. //更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值
    128. line *amendElem(line *p,int add,int newElem){
    129. line * temp=p;
    130. //遍历到被删除结点
    131. for(int i=1; i
    132. {
    133. temp=temp->next;
    134. }
    135. temp->data=newElem;
    136. return p;
    137. }
    138. //输出链表的功能函数
    139. void display(line *head)
    140. {
    141. line *temp=head;
    142. while(temp)
    143. {
    144. if(temp->next==NULL)
    145. {
    146. printf("%d\n",temp->data);
    147. }
    148. else
    149. {
    150. printf("%d->",temp->data);
    151. }
    152. temp=temp->next;
    153. }
    154. }

    参考文献:http://c.biancheng.net/view/3343.html

    好啦,以上就是本文的全部内容啦!创作不易,点个赞再走呗~

  • 相关阅读:
    【Kafka】SpringBoot整合Kafka
    C++引用
    算法开篇——数组
    sqlmap 执行后打开其它程序
    【Pytorch】快速掌握迁移学习:代码示例与指南(使用预训练模型解决图像分类任务)
    研究生如何选择适合自己的导师
    计算机毕业设计Java海城同泽中学图书仓库管理系统(源码+系统+mysql数据库+lw文档)
    ubuntu18.04服务搭建yolov5开发环境
    多旋翼无人机仿真 rotors_simulator:用键盘控制无人机飞行
    html通过使用图像源的协议(protocol)相对 URL 来防止安全/不安全错误
  • 原文地址:https://blog.csdn.net/weixin_46522531/article/details/126571624