找回密码
 立即注册
首页 业界区 科技 双向链表的初始化、插入、删除、遍历

双向链表的初始化、插入、删除、遍历

何书艺 2025-7-2 18:04:07
  1. /**
  2. * @file name : DoubleLnList.c
  3. * @brief     :  实现一个双向不循环链表的接口,可实现链表的增删改查,另外为了提高可移植性,所以链表中
  4. *               数据元素的类型为DataType_t,用户可以根据实际情况修改链表中元素的类型
  5. * @author    :  MINDSETT@163.com
  6. * @date      :  2025/6/30
  7. * @version   :  1.0
  8. * @note      :  特别说明:此接口实现的双向链表中首结点的直接前驱指针指向NULL,并不是头结点的地址
  9. * CopyRight (c)  2025  MINDSETT@163.com  All Right Reserved
  10. */
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <stdbool.h>
  14. //指定链表中元素的数据类型,用户可根据需要进行修改
  15. typedef int DataType_t;
  16. //构造链表的结点,链表中所有结点的数据类型应该是相同的(数据域+指针域)
  17. typedef struct DoubleLinkedList
  18. {
  19.     DataType_t                  data;   //结点的数据域
  20.     struct DoubleLinkedList *   prev;   //直接前驱指针域
  21.     struct DoubleLinkedList *   next;   //直接后继的指针域
  22. }DoubleLnList_t;  
  23. /**
  24. * @name    :  DoubleLnList_Create
  25. * @brief   :  创建一个空链表,空链表应该有一个头节点,对链表进行初始化
  26. * @param   :  None
  27. * @retval  :  返回头结点的地址
  28. * @date    :  2025/6/30
  29. * @version :  1.0
  30. * @note    :  None
  31. */
  32. DoubleLnList_t *DoubleLnList_Create(void)
  33. {
  34.     //创建一个头结点并对头结点申请内存
  35.     DoubleLnList_t* Head=(DoubleLnList_t*)calloc(1,sizeof(DoubleLnList_t));
  36.     if (NULL==Head){
  37.         perror("calloc memory for Head is failed\n");
  38.         exit(-1);
  39.     }
  40.     //对头结点进行初始化,头结点不存储数据域,指针域指向NULL
  41.     Head->prev=NULL;
  42.     Head->next=NULL;
  43.     //将头结点的地址返回
  44.     return Head;
  45. }
  46. /**
  47. * @name    :  DoubleLnList_NewNode
  48. * @brief   :  创建一个新结点,并为新结点申请堆内存以及对新结点的数据域和指针域进行初始化
  49. * @param   :  
  50. *             @data:新结点的数据
  51. * @retval  :  返回新结点的地址
  52. * @date    :  2025/6/30
  53. * @version :  1.0
  54. * @note    :  None
  55. */
  56. DoubleLnList_t * DoubleLnList_NewNode(DataType_t data)
  57. {
  58.     //创建新结点,并未新结点申请堆内存
  59.     DoubleLnList_t * New=(DoubleLnList_t*)calloc(1,sizeof(DoubleLnList_t));
  60.      if (NULL==New){
  61.         perror("calloc memory for NewNode is failed\n");
  62.         return NULL;
  63.     }
  64.     //对新结点的数据域和指针域进行初始化
  65.     New->data=data;
  66.     New->prev=NULL;
  67.     New->next=NULL;
  68.     return New;
  69. }
  70. /**
  71. * @name    :  DoubleLnList_FirstInsert
  72. * @brief   :  创建一个新结点,并把新结点插入链表的首部
  73. * @param   :  
  74. *             @Head:插入的链表
  75. *             @data:新结点的数据
  76. * @retval  :  功返回true,失败返回false
  77. * @date    :  2025/6/30
  78. * @version :  1.0
  79. * @note    :  None
  80. */
  81. bool DoubleLnList_FirstInsert(DoubleLnList_t * Head,DataType_t data)
  82. {
  83.     //创建新结点,并对新结点进行初始化
  84.     DoubleLnList_t* New=DoubleLnList_NewNode(data);
  85.     if (NULL==New){
  86.         perror("Create New node is failed\n");
  87.         return false;
  88.     }
  89.     //判断链表是否为空,如果为空,则将新结点直接插入
  90.     if (NULL==Head->next){
  91.         Head->next=New;
  92.         return true;
  93.     }
  94.     New->next=Head->next;//新结点的next指向原首结点
  95.     Head->next->prev=New;//原首结点的prev指向新结点
  96.     Head->next=New;//头结点的next指向新结点
  97.     return true;
  98. }
  99. /**
  100. * @name    :  DoubleLnList_tailInsert
  101. * @brief   :  创建一个新结点,并把新结点插入链表的尾部
  102. * @param   :  
  103. *             @Head:插入的链表
  104. *             @data:新结点的数据
  105. * @retval  :  成功返回true,失败返回false
  106. * @date    :  2025/6/30
  107. * @version :  1.0
  108. * @note    :  None
  109. */
  110. bool DoubleLnList_tailInsert(DoubleLnList_t * Head,DataType_t data)
  111. {
  112.     //创建新结点,并对新结点进行初始化
  113.     DoubleLnList_t* New=DoubleLnList_NewNode(data);
  114.     if (NULL==New){
  115.         perror("Create New node is failed\n");
  116.         return false;
  117.     }
  118.     //判断链表是否为空,如果为空,则将新结点直接插入
  119.     if (NULL==Head->next){
  120.         Head->next=New;
  121.         return true;
  122.     }
  123.     //备份首结点的地址
  124.     DoubleLnList_t * tail=Head->next;
  125.     //找到尾结点
  126.     while(tail->next){
  127.         tail=tail->next;
  128.     }
  129.     tail->next=New;//尾结点的next指向新结点
  130.     New->prev=tail;//新结点的prev指向尾结点
  131.     return true;
  132. }
  133. /**
  134. * @name    :  DoubleLnList_DestInsert
  135. * @brief   :  创建一个新结点,并把新结点插入链表的指定位置
  136. * @param   :  
  137. *             @Head:插入的链表
  138. *             @Dest:指定需要新结点插入位置的直接前驱的数据
  139. *             @data:新结点的数据
  140. * @retval  :  成功返回true,失败返回false
  141. * @date    :  2025/6/30
  142. * @version :  1.0
  143. * @note    :  None
  144. */
  145. bool DoubleLnList_DestInsert(DoubleLnList_t * Head,DataType_t Dest,DataType_t data)
  146. {
  147.     //创建新结点,并对新结点进行初始化
  148.     DoubleLnList_t* New=DoubleLnList_NewNode(data);
  149.     if (NULL==New){
  150.         perror("Create New node is failed\n");
  151.         return false;
  152.     }
  153.     //判断链表是否为空,如果为空,则将新结点直接插入
  154.     if (NULL==Head->next){
  155.         Head->next=New;
  156.         return true;
  157.     }
  158.     //备份首结点的地址
  159.     DoubleLnList_t * curr=Head->next;
  160.     //如果链表为非空,遍历链表,目的是找到目标结点
  161.     while( NULL!=curr && Dest!=curr->data){
  162.         curr=curr->next;
  163.     }
  164.     //未找到目标结点,错误输出
  165.     if ( NULL==curr ){
  166.         printf("Dest is not found\n");
  167.         free(New);
  168.         return false;
  169.     }
  170.     if(curr->next){      
  171.         New->next=curr->next;//当前结点并不是尾结点时,新结点的next指向目标节点的next   
  172.         curr->next->prev=New;//当前结点并不是尾结点时,目标结点的直接后继的prev指向新结点
  173.     }
  174.     New->prev=curr;         //新结点的prev指向目标结点
  175.     curr->next=New;         //目标结点的next指向新结点
  176.         
  177.     return true;
  178. }
  179. /**
  180. * @name    :  DoubleLnList_FirstDel
  181. * @brief   :  删除链表的首结点
  182. * @param   :  
  183. *             @Head:链表的头结点
  184. * @retval  :  成功返回true,失败返回false
  185. * @date    :  2025/6/30
  186. * @version :  1.0
  187. * @note    :  None
  188. */
  189. bool DoubleLnList_FirstDel(DoubleLnList_t* Head)
  190. {
  191.     //备份首结点
  192.     DoubleLnList_t * first=Head->next;
  193.     //判断传参错误或链表为空
  194.     if (NULL==Head || NULL==Head->next){
  195.         perror("Linked list is empty\n");
  196.         return false;
  197.     }
  198.     //1.只有一个首结点
  199.     if (Head->next->next==NULL){
  200.         Head->next=NULL;
  201.         first->next=NULL;//首结点的next指向NULL,从链表中断开(避免野指针)
  202.         free(first);
  203.         return true;
  204.     }
  205.     //2.链表有多个结点
  206.     Head->next->next->prev=NULL;//原首结点的直接后继的prev指向NULL
  207.     Head->next=Head->next->next;//头结点的next指向原首结点的直接后继
  208.     //旧的首结点的指针域指向NULL,从链表中断开(避免野指针)
  209.     first->next=NULL;
  210.     //释放首结点占用的内存
  211.     free(first);
  212.     return true;
  213. }
  214. /**
  215. * @name    :  DoubleLnList_tailDel
  216. * @brief   :  删除链表的尾结点
  217. * @param   :  
  218. *             @Head:链表的头结点
  219. * @retval  :  成功返回true,失败返回false
  220. * @date    :  2025/6/30
  221. * @version :  1.0
  222. * @note    :  None
  223. */
  224. bool DoubleLnList_tailDel(DoubleLnList_t* Head)
  225. {
  226.     //备份头结点和首结点
  227.     DoubleLnList_t * prev=Head;
  228.     DoubleLnList_t * tail=Head->next;
  229.     //判断传参错误或链表为空
  230.     if (NULL==Head || NULL==Head->next){
  231.         perror("Linked list is empty\n");
  232.         return false;
  233.     }
  234.     //1.只有一个首结点
  235.     if (Head->next->next==NULL){
  236.         Head->next=NULL;
  237.         tail->next=NULL;//首结点的next指向NULL,从链表中断开(避免野指针)
  238.         free(tail);
  239.         return true;
  240.     }
  241.     //2.链表有多个结点
  242.     //找到尾结点和它的直接前驱
  243.     while(tail->next){
  244.         prev=tail;
  245.         tail=tail->next;
  246.     }
  247.     //尾结点的直接前驱的prev指向NULL
  248.     prev->next=NULL;
  249.     //旧的尾结点的指针域指向NULL,从链表中断开(避免野指针)
  250.     tail->prev=NULL;
  251.     free(tail);
  252.     return true;
  253. }
  254. /**
  255. * @name    :  DoubleLnList_DestDel
  256. * @brief   :  删除链表的指定结点
  257. * @param   :  
  258. *             @Head:链表的头结点
  259. *             @Dest:需要删除的指定结点
  260. * @retval  :  功返回true,失败返回false
  261. * @date    :  2025/6/30
  262. * @version :  1.0
  263. * @note    :  None
  264. */
  265. bool DoubleLnList_DestDel(DoubleLnList_t* Head,DataType_t Dest)
  266. {
  267.     //备份头结点和首结点
  268.     DoubleLnList_t * prev=Head;
  269.     DoubleLnList_t * curr=Head->next;
  270.     //判断传参错误或链表为空
  271.     if (NULL==Head || NULL==Head->next){
  272.         perror("Linked list is empty\n");
  273.         return false;
  274.     }
  275.     //1.只有一个首结点
  276.     if (Head->next->next==NULL){
  277.         Head->next=NULL;
  278.         curr->next=NULL;//首结点的指针域指向NULL,从链表中断开(避免野指针)
  279.         free(curr);
  280.         return true;
  281.     }
  282.     //2.链表有多个结点
  283.     //遍历链表,目的是找到目标结点和它的直接前驱
  284.     while( NULL!=curr && Dest!=curr->data){
  285.         prev=curr;
  286.         curr=curr->next;
  287.     }
  288.     //未找到目标结点,错误输出
  289.     if( NULL==curr ){
  290.         printf("Dest is not found\n");
  291.         return false;
  292.     }
  293.    
  294.     //找到目标结点
  295.     prev->next=curr->next;//目标结点的直接前驱的next指向目标结点的直接后继
  296.     if(curr->next){
  297.         if(curr==Head->next){
  298.             curr->next->prev=NULL;//当前结点是首结点时,目标结点的直接后继的prev指向NULL
  299.         }else{
  300.             curr->next->prev=prev;//当前结点并不是首结点或尾结点时,目标结点的直接后继的prev指向目标结点的直接前驱
  301.         }
  302.     }
  303.     //目标结点的指针域指向NULL,从链表中断开(避免野指针)
  304.     curr->prev=NULL;
  305.     curr->next=NULL;
  306.     free(curr);
  307.     return true;
  308. }
  309. /**
  310. * @name    :  DoubleLnList_Print
  311. * @brief   :  遍历输出链表中各个结点的数据
  312. * @param   :  
  313. *             @Head:需要遍历的链表
  314. * @retval  :  成功返回true,失败返回false
  315. * @date    :  2025/6/30
  316. * @version :  1.0
  317. * @note    :  None
  318. */
  319. bool DoubleLnList_Print(DoubleLnList_t* Head)
  320. {
  321.     //判断传参错误或链表为空
  322.     if (NULL==Head || NULL==Head->next){
  323.         perror("CurrentDoubleLinkedList is empty\n");
  324.         return false;
  325.     }
  326.     //对链表的头节点的地址进行备份
  327.     DoubleLnList_t *curr=Head;
  328.     //循环遍历链表
  329.     while( curr->next){
  330.         curr=curr->next;
  331.         printf("Data=%d ",curr->data);
  332.     }
  333.     return true;
  334. }
复制代码
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册