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

单链表的定义与基本操作

思矿戳 2025-8-8 14:19:54
单链表操作实现

1.什么是单链表?

单链表是一种常见的线性数据结构,由一系列节点组成,每个节点包含两个部分:数据域指针域。数据域存储实际数据,指针域指向下一个节点。在单链表中,数据元素可以非连续地存储在内存中,而节点之间通过指针相互连接。
1.png

2.代码实现

链表的创建、插入、删除、查找等常用操作。
  1. #include <stdio.h>
  2. #include <stdbool.h>
  3. #include <stdlib.h>
  4. typedef int DataType_t;
  5. //构造链表的节点,列表中所有的数据类型应该是相同的
  6. typedef struct LinkedList
  7. {
  8.         DataType_t                        data;//节点的数据域
  9.         struct LinkedList  *next;//结点的指针域
  10. }LList_t;
  11. //创建一个空链表,空链表应该有一个头节点,对链表进行初始化
  12. LList_t* LList_Create(void)
  13. {
  14.         //1.给头节点申请空间
  15.         LList_t *Head = (LList_t *)calloc(1,sizeof(LList_t));
  16.         if(NULL == Head)
  17.         {
  18.                 perror("Calloc memory for the Head is failed!\n");
  19.                 exit(-1);
  20.         }
  21.         //头节点初始化,头节点是不存储有效内容
  22.         Head->next = NULL;
  23.         return Head;
  24. }
  25. //生成新的有效结点
  26. LList_t* LList_NewNode(DataType_t data)
  27. {
  28.         LList_t *New = (LList_t *)calloc(1,sizeof(LList_t));
  29.         if(NULL == New)
  30.         {
  31.                 perror("Calloc memory for the New is failed!\n");
  32.                 exit(-1);
  33.         }
  34.     //新节点初始化
  35.         New->next = NULL;
  36.         New->data = data;
  37.         return New;
  38. }
  39. //在链表头部插入结点
  40. bool LList_HeadInsert(LList_t *Head,DataType_t data)
  41. {
  42.         LList_t *New = LList_NewNode(data);
  43.        
  44.         if(NULL == New)
  45.         {
  46.                 perror("Create NewNode is Failed !\n");
  47.                 return false;
  48.         }
  49.     //若链表无有效结点
  50.         if(NULL == Head->next)
  51.         {
  52.                 Head->next = New;
  53.                 return true;
  54.         }
  55.     //在头结点后插入结点
  56.         New->next  = Head->next;
  57.         Head->next = New;
  58.         return true;
  59. }
  60. //遍历链表每个结点
  61. void LList_Print(LList_t *Head)
  62. {
  63.     if (Head == NULL || Head->next == NULL) {
  64.         printf("链表为空!\n");
  65.         return;
  66.     }
  67.    
  68.     LList_t *current = Head->next;
  69.     printf("链表内容: ");
  70.     while (current != NULL) {
  71.         printf("%d ", current->data);
  72.         current = current->next;
  73.     }
  74.     printf("\n");
  75. }
  76. //在链表尾部插入结点
  77. bool LList_TailInsert(LList_t *Head,DataType_t data)
  78. {
  79.         LList_t *New = LList_NewNode(data);
  80.         if(NULL == New)
  81.         {
  82.                 perror("Create NewNode is Failed !\n");
  83.                 return false;
  84.         }
  85.     //找到最后一个结点位置
  86.         while(Head->next != NULL)
  87.         {
  88.                 Head = Head->next;
  89.         }
  90.         Head->next = New;
  91.         return true;
  92. }
  93. //根据结点数据域的值来指定在目标结点后插入新结点
  94. bool LList_DestInsert(LList_t *Head,DataType_t dest,DataType_t data)
  95. {
  96.        
  97.         LList_t *New = LList_NewNode(data);
  98.        
  99.         if(NULL == New)
  100.         {
  101.                 perror("Create NewNode is Failed !\n");
  102.                 return false;
  103.         }
  104.     //链表只有头结点,新结点直接放在头结点之后
  105.         if(NULL == Head->next)
  106.         {
  107.                 Head->next = New;
  108.                 return true;
  109.         }
  110.         LList_t *current = Head->next;
  111.         while((current != NULL) && (current->data != dest))
  112.         {
  113.                 current = current->next;
  114.         }
  115.         //current指到了最后一个结点的next
  116.          if (current == NULL)
  117.     {
  118.         printf("Destination node not found!\n");
  119.         free(New); // 插入失败,释放已申请的内存
  120.         return false;
  121.     }
  122.         //找到了数据域data值等于dest的结点
  123.         New ->next = current->next;
  124.         current->next = New;
  125.         return true;
  126. }
  127. //删除链表尾部结点
  128. bool LList_TailDel(LList_t *Head)
  129. {
  130.     if(NULL == Head->next)
  131.     {
  132.         printf("链表空!\n");
  133.         return false;
  134.     }
  135.     LList_t *current    = Head->next;
  136.     LList_t *ex_current = Head; // 初始化为Head,用于删除操作
  137.    
  138.     // 遍历链表,找到尾节点和它的前一个节点
  139.     while(current->next != NULL)
  140.     {   
  141.         ex_current = current;
  142.         current = current->next;
  143.     }
  144.    
  145.     // 删除尾节点
  146.     ex_current->next = NULL; // 断开与尾节点的连接
  147.     free(current);           // 释放尾节点的内存
  148.     return true;
  149. }
  150. //删除链表头(头结点之后)的结点
  151. bool LList_HeadDel(LList_t *Head)
  152. {
  153.         if(NULL == Head->next)
  154.         {
  155.                 printf("链表空!\n");
  156.                 return false;
  157.         }
  158.         LList_t *current = Head->next;
  159.         Head->next = Head->next->next;
  160.         free(current);
  161.         current = NULL;
  162.         return true;
  163. }
  164. //根据结点数据域的值来删除指定目标结点
  165. bool LList_DestDel(LList_t *Head, DataType_t dest)
  166. {
  167.     if (NULL == Head->next)
  168.     {
  169.         printf("链表空!\n");
  170.         return false;
  171.     }
  172.     LList_t *current = Head->next;
  173.     LList_t *ex_current = Head;
  174.     // 遍历链表,直到找到目标节点或链表结束
  175.     while (current != NULL && current->data != dest)
  176.     {
  177.         ex_current = current;
  178.         current = current->next;
  179.     }
  180.     // 检查是否找到目标节点
  181.     if (current == NULL)
  182.     {
  183.         printf("没有在链表找到要删除的指定结点!\n");
  184.         return false;
  185.     }
  186.     // 删除目标节点
  187.     ex_current->next = current->next;
  188.     free(current);
  189.     current = NULL;
  190.     return true;
  191. }
复制代码
其他辅助函数

1.删除最小值结点
  1. bool LList_MinDel(LList_t *Head)
  2. {
  3.     // 判断链表是否为空:如果头节点的 next 为 NULL,说明链表没有有效节点
  4.     if (NULL == Head->next)
  5.     {
  6.         printf("链表空!\n");
  7.         return false;
  8.     }
  9.     // 定义当前遍历节点 current,从第一个有效节点开始
  10.     LList_t *current     = Head->next;
  11.     LList_t *pre_current = Head;
  12.     // 定义最小值节点 MinNode,初始为第一个有效节点
  13.     LList_t *MinNode     = current;
  14.     LList_t *pre_MinNode = Head;
  15.     // 遍历链表,寻找最小值节点
  16.     while (current != NULL)
  17.     {
  18.         // 如果当前节点的 data 小于最小值节点的 data
  19.         if(current->data < MinNode->data)
  20.         {
  21.             // 更新最小值节点及其前驱节点
  22.             MinNode = current;
  23.             pre_MinNode = pre_current;
  24.         }
  25.         // 继续向后遍历
  26.         pre_current = current;
  27.         current = current->next;
  28.     }
  29.     // 找到最小值节点后,将其从链表中删除
  30.     pre_MinNode->next = MinNode->next;
  31.     free(MinNode);
  32.     return true;
  33. }
复制代码
2.查找倒数第 k 个节点(快慢指针法)
  1. int findKthFromEnd(LList_t *Head, int k)
  2. {
  3.     if (Head == NULL || k <= 0) {
  4.         printf("无效输入:k必须为正整数且链表不能为空\n");
  5.         return 0;
  6.     }
  7.     LList_t *fast = Head;
  8.     LList_t *slow = Head;
  9.     int steps = 0;
  10.     // fast 先走 k 步
  11.     while (steps < k) {
  12.         if (fast == NULL) {
  13.             printf("链表长度不足 %d 个节点,无法查找倒数第 %d 个节点\n", k, k);
  14.             return 0;
  15.         }
  16.         fast = fast->next;
  17.         steps++;
  18.     }
  19.     // 如果 fast 已经为 NULL,说明链表正好有 k 个节点
  20.     if (fast == NULL) {
  21.         printf("倒数第 %d 个结点的 data 值为: %d\n", k, slow->data);
  22.         return 1;
  23.     }
  24.     // 同时移动 fast 和 slow
  25.     while (fast != NULL) {
  26.         fast = fast->next;
  27.         slow = slow->next;
  28.     }
  29.     // 此时 slow 指向倒数第 k 个节点
  30.     printf("倒数第 %d 个结点的 data 值为: %d\n", k, slow->data);
  31.     return 1;
  32. }
复制代码
快慢指针经典算法。fast 先走 k 步,然后 slow 和 fast 一起走,当 fast 到达末尾时,slow 正好在倒数第 k 个位置。
3.主函数测试
  1. int main(int argc, char const *argv[])
  2. {
  3.     // 创建链表
  4.     printf("=== 创建空链表 ===\n");
  5.     LList_t *head = LList_Create();
  6.     if (head != NULL) {
  7.         printf("链表创建成功!\n");
  8.     }
  9.     LList_Print(head);
  10.    
  11.     // 测试头插法
  12.     printf("\n=== 测试头插法 ===\n");
  13.     LList_HeadInsert(head, 10);
  14.     LList_HeadInsert(head, 20);
  15.     LList_HeadInsert(head, 30);
  16.     LList_Print(head);  // 预期结果: 30 20 10
  17.    
  18.     // 测试尾插法
  19.     printf("\n=== 测试尾插法 ===\n");
  20.     LList_TailInsert(head, 40);
  21.     LList_TailInsert(head, 50);
  22.     LList_Print(head);  // 预期结果: 30 20 10 40 50
  23.    
  24.     // 测试指定位置插入
  25.     printf("\n=== 测试指定位置插入 ===\n");
  26.     LList_DestInsert(head, 20, 25);  // 在20后插入25
  27.     LList_Print(head);  // 预期结果: 30 20 25 10 40 50
  28.     LList_DestInsert(head, 100, 99); // 尝试插入到不存在的节点后
  29.    
  30.     // 测试查找倒数第k个节点
  31.     printf("\n=== 测试查找倒数第k个节点 ===\n");
  32.     findKthFromEnd(head, 1);  // 预期结果: 50
  33.     findKthFromEnd(head, 3);  // 预期结果: 10
  34.     findKthFromEnd(head, 10); // 预期结果: 链表长度不足
  35.    
  36.     // 测试删除头节点
  37.     printf("\n=== 测试删除头节点 ===\n");
  38.     LList_HeadDel(head);
  39.     LList_Print(head);  // 预期结果: 20 25 10 40 50
  40.    
  41.     // 测试删除尾节点
  42.     printf("\n=== 测试删除尾节点 ===\n");
  43.     LList_TailDel(head);
  44.     LList_Print(head);  // 预期结果: 20 25 10 40
  45.    
  46.     // 测试删除指定节点
  47.     printf("\n=== 测试删除指定节点 ===\n");
  48.     LList_DestDel(head, 25);
  49.     LList_Print(head);  // 预期结果: 20 10 40
  50.     LList_DestDel(head, 100); // 尝试删除不存在的节点
  51.    
  52.     // 添加几个节点用于测试删除最小值
  53.     printf("\n=== 测试删除最小值节点 ===\n");
  54.     LList_TailInsert(head, 5);
  55.     LList_HeadInsert(head, 35);
  56.     LList_TailInsert(head, 15);
  57.     LList_Print(head);  // 预期结果: 35 20 10 40 5 15
  58.     LList_MinDel(head); // 删除最小值5
  59.     LList_Print(head);  // 预期结果: 35 20 10 40 15
  60.     LList_MinDel(head); // 删除最小值10
  61.     LList_Print(head);  // 预期结果: 35 20 40 15
  62.    
  63.     // 释放链表内存
  64.     printf("\n=== 释放链表内存 ===\n");
  65.     while (head->next != NULL) {
  66.         LList_HeadDel(head);
  67.     }
  68.     free(head);
  69.     head = NULL;
  70.     printf("链表已释放\n");
  71.    
  72.     return 0;
  73. }
复制代码
测试结果:

2.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册