找回密码
 立即注册
首页 业界区 业界 双向链表的定义与基本操作

双向链表的定义与基本操作

洫伍俟 2025-8-9 20:17:27
双向链表操作实现

双向链表

双向链表(Doubly Linked List)是一种链式数据结构,其中的每个节点不仅指向下一个节点,还指向前一个节点。这与单向链表不同,后者每个节点只包含到下一个节点的引用。双向链表因此允许在两个方向上遍历:向前和向后。
每个节点在双向链表中通常包含三部分:

  • 指向前一个节点的引用(或指针)。
  • 存储的数据元素。
  • 指向下一个节点的引用(或指针)。
双向链表的优点包括:

  • 可以从任意一个节点出发,既能够向前访问也能够向后访问,增加了灵活性。
  • 在已知节点位置的情况下,插入和删除操作可以高效地进行,因为只需要改变相应节点的前驱和后继的指针即可。
缺点是由于每个节点需要额外存储指向前一个节点的指针,因此相比单向链表会占用更多的内存空间。
代码实现

双向链表的创建、插入、删除、查找等常用操作。
  1. #include <stdio.h>        // 标准输入输出库,用于 printf 等函数
  2. #include <stdbool.h>        // 布尔类型库,提供 bool、true、false
  3. #include <stdlib.h>        // 标准库,提供 malloc/calloc/free 和 exit 等函数
  4. //定义链表中数据域的类型为 int
  5. typedef int DataType_t;
  6. //定义双向链表的结点结构体
  7. typedef struct DoubleLinkedList
  8. {
  9.         DataType_t                       data; //结点的数据域
  10.         struct DoubleLinkedList        *prev; //直接前驱的指针域
  11.         struct DoubleLinkedList        *next; //直接后继的指针域
  12. }DoubleLList_t;
  13. //创建头结点:初始化一个空的双向链表
  14. DoubleLList_t* DoubleLList_Create(void)
  15. {
  16.     // 为头结点动态分配内存,并初始化为 0(calloc 会清零)
  17.         DoubleLList_t *Head = (DoubleLList_t *)calloc(1,sizeof(DoubleLList_t));
  18.          // 如果内存分配失败,打印错误信息并退出程序
  19.     if(Head == NULL){
  20.                 perror("Calloc memory for the Head is failed!\n");
  21.                 exit(-1);
  22.         }
  23.         Head->next = NULL;
  24.         Head->prev = NULL;
  25.         return Head;
  26. }
  27. //创建一个新结点(仅数据域赋值,指针域初始化为 NULL)
  28. DoubleLList_t* DoubleLList_NewNode(DataType_t data)
  29. {
  30.         //1.创建一个新结点并对新结点申请内存
  31.         DoubleLList_t *New = (DoubleLList_t *)calloc(1,sizeof(DoubleLList_t));
  32.         if (NULL == New)
  33.         {
  34.                 perror("Calloc memory for NewNode is Failed");
  35.                 return NULL;
  36.         }
  37.         //2.对新结点的数据域和指针域(2个)进行初始化
  38.         New->data = data;
  39.         New->prev = NULL;
  40.         New->next = NULL;
  41.         return New;
  42. }
  43. // 头插法:在链表头部(第一个有效结点位置)插入新元素
  44. bool DbleLList_HeadInsert(DoubleLList_t* Head,DataType_t data)
  45. {
  46.     // 创建新结点
  47.         DoubleLList_t *New = DoubleLList_NewNode(data);
  48.           if (New == NULL){
  49.       return false;
  50.           }
  51.     // 若为空,新结点直接作为第一个有效结点
  52.         if(Head->next == NULL){
  53.                 Head->next = New;
  54.         }
  55.         else{// 否则,将新结点插入到首结点之前
  56.                 New->next = Head->next;
  57.                 Head->next->prev = New;
  58.                 Head->next = New;
  59.         }
  60.         return true;// 插入成功
  61. }
  62. // 尾插法:在链表尾部插入新元素
  63. bool DbleLList_TailInsert(DoubleLList_t* Head,DataType_t data)
  64. {
  65.         DoubleLList_t *New = DoubleLList_NewNode(data);
  66.   if (New == NULL) {
  67.       return false;
  68.   }
  69.   //如果只有头结点,New直接成为首结点
  70.         if(Head->next == NULL){//如果只有头结点,New直接成为首结点
  71.                 Head->next = New;
  72.         }
  73.         else{// 否则,从首结点开始遍历,寻找最后一个结点
  74.                 DoubleLList_t *Find_Tail = Head->next;
  75.                 while(Find_Tail->next != NULL){
  76.                         Find_Tail = Find_Tail->next;
  77.                 }
  78.          // 找到尾结点后,将新结点连接在其后
  79.                 Find_Tail->next = New;
  80.                 New->prev = Find_Tail;               
  81.         }
  82.         return true;
  83. }
  84. //指定在链表中某个结点后插入一个新结点
  85. bool DbleLList_DestInsert_l(DoubleLList_t* Head,DataType_t dest,DataType_t data)
  86. {
  87.         DoubleLList_t *New = DoubleLList_NewNode(data);
  88.           if (New == NULL) {
  89.     return false;
  90.           }
  91.         if(Head->next == NULL){//如果只有头结点,New直接成为首结点
  92.                 Head->next = New;
  93.         }
  94.         DoubleLList_t *Find_Dest = Head->next;
  95.         //遍历链表,找到数据域data等于目标值dest的结点
  96.         while(Find_Dest->data != dest){       
  97.                 if (Find_Dest->next == NULL){//若遍历到链表最后一个结点,data和dest还不相等,说明dest为无效指定结点位置
  98.                
  99.                         printf("无效指定结点位置\n");
  100.                         return false;
  101.                 }
  102.                 Find_Dest = Find_Dest->next;
  103.         }
  104.         if (Find_Dest->next == NULL){//若遍历到链表最后一个结点,数据域data等于dest,新结点New成为尾结点
  105.        
  106.                 Find_Dest->next = New;
  107.                 New->prev = Find_Dest;
  108.         }
  109.         else{
  110.                 New->next = Find_Dest->next;
  111.                 Find_Dest->next->prev = New;
  112.                 New->prev = Find_Dest;
  113.                 Find_Dest->next = New;
  114.         }
  115.         return true;
  116. }
  117. //指定在链表中某个结点前插入一个新结点
  118. bool DbleLList_DestInsert_f(DoubleLList_t *Head,DataType_t dest,DataType_t data)
  119. {
  120.         DoubleLList_t *New = DoubleLList_NewNode(data);
  121.           if (New == NULL) {
  122.     return false;
  123.           }
  124.         if(Head->next == NULL){//如果只有头结点,New直接成为首结点
  125.                 Head->next = New;
  126.         }
  127.         DoubleLList_t *Find_Dest = Head->next;
  128.         //遍历链表,找到数据域data等于目标值dest的结点
  129.         while(Find_Dest->data != dest){       
  130.                 if (Find_Dest->next == NULL){//若遍历到链表最后一个结点,data和dest还不相等,说明dest为无效指定结点位置
  131.                
  132.                         printf("无效指定结点位置\n");
  133.                         return false;
  134.                 }
  135.                 Find_Dest = Find_Dest->next;
  136.         }
  137.         //1.首结点是目标结点
  138.         if(Find_Dest == Head->next){
  139.                 New->next = Head->next;
  140.                 Head->next->prev = New;
  141.                 Head->next = New;       
  142.         }
  143.         else{
  144.                 New->prev = Find_Dest->prev;
  145.                 Find_Dest->prev->next =New;
  146.                 New->next = Find_Dest;
  147.                 Find_Dest->prev = New;
  148.         }
  149.         return true;
  150. }
  151. //遍历链表并输出所有结点的data值
  152. bool DbleLList_Print(DoubleLList_t *Head)
  153. {
  154.         DoubleLList_t *Phead = Head;
  155.         if(Head->next == NULL){
  156.                 printf("current DoubleLList is empty!\n");
  157.                 return false;
  158.         }
  159.         printf("链表内容: ");
  160.         while(Phead->next){
  161.                 Phead = Phead->next;
  162.                 printf("%d ",Phead->data);
  163.                 if(Phead->next == NULL){       
  164.                         break;
  165.                 }
  166.         }
  167.         printf("\n");
  168.         return true;
  169. }
  170. //删除链表头结点
  171. bool DbleLList_HeadDel(DoubleLList_t *Head)
  172. {
  173.         DoubleLList_t *Temp  = Head->next;
  174.         //if链表只有一个头结点
  175.         if(Head->next == NULL){
  176.                 printf("current DoubleLList is empty!\n");
  177.                 return false;
  178.         }
  179.         //if当前链表只有一个首结点
  180.         if(Temp->next == NULL){
  181.                 Head->next = NULL;
  182.                 free(Temp);
  183.                 return true;
  184.         }
  185.         //新的首结点pre指向NULL不指向Head
  186.         Temp->next->prev = NULL;
  187.         //头结点next指向要删除结点的next
  188.         Head->next = Temp->next;
  189.        
  190.         free(Temp);
  191.         return true;
  192. }
  193. //指定位置删除链表结点
  194. bool DbleLList_del_dest(DoubleLList_t* Head,DataType_t dest)
  195. {       
  196.         if(Head->next == Head){
  197.                 printf("current DoubleLList is empty!\n");
  198.                 return false;
  199.         }
  200.         DoubleLList_t *Find_Dest = Head->next;
  201.         while(Find_Dest->data != dest){       
  202.                 if (Find_Dest->next == NULL){//若遍历到链表最后一个结点
  203.                         printf("未找到指定节点\n");
  204.                         return false;
  205.                 }
  206.                 Find_Dest = Find_Dest->next;
  207.         }
  208.         if (Head->next == Find_Dest){//若目标结点是链表第一个结点
  209.                 if(Find_Dest->next = NULL){//若目标结点还是是链表唯一结点
  210.                         Head->next = NULL;
  211.                 }
  212.                 else{
  213.                 Find_Dest->next->prev = NULL;
  214.                 Head->next = Find_Dest->next;
  215.                 }
  216.         }
  217.         else{//若目标结点是链表最后一个结点
  218.                 if(Find_Dest->next == NULL){
  219.                         Find_Dest->prev->next = NULL;
  220.                 }
  221.                 else{//若目标结点有前驱后继
  222.                         Find_Dest->prev->next = Find_Dest->next;
  223.                         Find_Dest->next->prev = Find_Dest->prev;
  224.                 }
  225.         }
  226.         free(Find_Dest);
  227.         return true;
  228. }
复制代码
主函数测试
  1. int main(int argc, char const *argv[])
  2. {
  3.     // 创建双向链表
  4.     DoubleLList_t *Head = DoubleLList_Create();
  5.     printf("1. 创建双向链表完成\n");
  6.     DbleLList_Print(Head);
  7.    
  8.     // 测试头插法
  9.     printf("\n2. 测试头插法插入 10, 20, 30\n");
  10.     DbleLList_HeadInsert(Head, 10);
  11.     DbleLList_HeadInsert(Head, 20);
  12.     DbleLList_HeadInsert(Head, 30);
  13.     DbleLList_Print(Head); // 预期结果: 30 20 10
  14.    
  15.     // 测试尾插法
  16.     printf("\n3. 测试尾插法插入 40, 50\n");
  17.     DbleLList_TailInsert(Head, 40);
  18.     DbleLList_TailInsert(Head, 50);
  19.     DbleLList_Print(Head); // 预期结果: 30 20 10 40 50
  20.    
  21.     // 测试指定节点后插入
  22.     printf("\n4. 测试在20后插入25\n");
  23.     DbleLList_DestInsert_l(Head, 20, 25);
  24.     DbleLList_Print(Head); // 预期结果: 30 20 25 10 40 50
  25.    
  26.     // 测试指定节点前插入
  27.     printf("\n5. 测试在40前插入35\n");
  28.     DbleLList_DestInsert_f(Head, 40, 35);
  29.     DbleLList_Print(Head); // 预期结果: 30 20 25 10 35 40 50
  30.    
  31.     // 测试删除头节点
  32.     printf("\n6. 测试删除头节点\n");
  33.     DbleLList_HeadDel(Head);
  34.     DbleLList_Print(Head); // 预期结果: 20 25 10 35 40 50
  35.    
  36.     // 测试删除指定节点
  37.     printf("\n7. 测试删除节点10\n");
  38.     DbleLList_del_dest(Head, 10);
  39.     DbleLList_Print(Head); // 预期结果: 20 25 35 40 50
  40.    
  41.     // 测试删除尾节点
  42.     printf("\n8. 测试删除尾节点(50)\n");
  43.     DbleLList_del_dest(Head, 50);
  44.     DbleLList_Print(Head); // 预期结果: 20 25 35 40
  45.    
  46.     // 测试删除中间节点
  47.     printf("\n9. 测试删除中间节点(25)\n");
  48.     DbleLList_del_dest(Head, 25);
  49.     DbleLList_Print(Head); // 预期结果: 20 35 40
  50.    
  51.     // 测试删除所有节点
  52.     printf("\n10. 测试删除所有节点\n");
  53.     DbleLList_HeadDel(Head);
  54.     DbleLList_HeadDel(Head);
  55.     DbleLList_HeadDel(Head);
  56.     DbleLList_Print(Head); // 预期结果: 空链表
  57.    
  58.     // 释放头节点
  59.     free(Head);
  60.     Head = NULL;
  61.     printf("\n11. 释放链表完成\n");
  62.    
  63.     return 0;
  64. }
复制代码
测试结果

1.png


来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

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