CExercise_11_单链表 手动实现一条单链表

打印 上一主题 下一主题

主题 1600|帖子 1600|积分 4800

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
标题:

基于以下头文件,手动实现一条单链表:
  1. // 头文件保护语法
  2. #ifndef LINKED_LIST_H
  3. #define LINKED_LIST_H
  4. // 包含linked_list.h头文件也会同步包含它包含的其它头文件
  5. // 根据这一特点, 可以选择将一些共用的头文件包含在此头文件中
  6. #include <stdbool.h>
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. typedef int DataType;
  10. // 定义链表结点结构
  11. typedef struct node {
  12.         DataType data;                // 数据域c
  13.         struct node *next;  // 指针域
  14. } Node;
  15. // 定义链表结构本身
  16. typedef struct {
  17.         Node *head;                // 头指针
  18.         Node *tail;                // 尾结点指针
  19.         int size;                // 用于记录链表的长度
  20. } LinkedList;
  21. // 创建一个空的链表
  22. LinkedList *create_linked_list();
  23. // 销毁链表
  24. void destroy_linked_list(LinkedList *list);
  25. // 头插法
  26. bool add_before_head(LinkedList *list, DataType new_data);
  27. // 尾插法
  28. bool add_behind_tail(LinkedList *list, DataType new_data);
  29. // 根据索引插入一个新结点
  30. bool add_by_idx(LinkedList *list, int idx, DataType new_data);
  31. // 根据索引搜索一个结点
  32. Node *search_by_idx(LinkedList *list, int idx);
  33. // 根据数据搜索一个结点
  34. Node *search_by_data(LinkedList *list, DataType data);
  35. // 根据数据删除一个结点
  36. bool delete_by_data(LinkedList *list, DataType data);
  37. // 扩展: 根据索引删除一个结点
  38. bool delete_by_idx(LinkedList *list, int idx);
  39. // 打印单链表 格式为:1 -> 2 -> 3 ->...
  40. void print_list(LinkedList *list);
  41.                
  42. #endif // !LINKED_LIST_H
  43. 手动实现单链表是后续一切数据结构学习的前提和基础,所以一定要重视单链表的实现,一定要自己百分百手动将这里面的函数全部实现。
复制代码

关键点

  

分析:

   :
  
代码

  1. //实现源文件的代码
  2. #include "linkedlist.h"
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. // 创建一个空的链表
  6. LinkedList *create_linked_list() {
  7.     return calloc(1, sizeof(LinkedList));
  8. }
  9. // 销毁链表
  10. void destroy_linked_list(LinkedList *list) {
  11.     // 遍历整条链表,包括最后一个结点,然后逐一free
  12.     Node *curr = list->head;
  13.     while (curr != NULL) {
  14.         Node *tmp = curr->next;
  15.         free(curr);
  16.         curr = tmp;
  17.     }
  18.     free(list);
  19. }
  20. // 头部插入结点
  21. bool add_before_head(LinkedList *list, DataType data) {
  22.     // 1.分配一个新结点,初始化它
  23.     Node *new_node = malloc(sizeof(Node));
  24.     if (new_node == NULL) {
  25.         printf("error: malloc failed in add_before_head.\n");
  26.         return false;
  27.     }
  28.     new_node->data = data;
  29.     // 2.让新结点指向原本的第一个结点
  30.     new_node->next = list->head;
  31.     // 3.更新头指针
  32.     list->head = new_node;
  33.     // 考虑特殊情况
  34.     //if(list->size == 0)
  35.     if (list->tail == NULL) {
  36.         // 说明头插之前链表为空, 更新尾指针
  37.         list->tail = new_node;
  38.     }
  39.     list->size++;
  40.     return true;
  41. }
  42. // 尾部插入结点
  43. bool add_behind_tail(LinkedList *list, DataType data) {
  44.     // 1.分配一个新结点,初始化它
  45.     Node *new_node = malloc(sizeof(Node));
  46.     if (new_node == NULL) {
  47.         printf("error: malloc failed in add_behind_tail.\n");
  48.         return false;
  49.     }
  50.     new_node->data = data;
  51.     new_node->next = NULL;
  52.     // 2.判断链表插入之前是否为空
  53.     if (list->size == 0) {
  54.         // 尾插之前链表为空
  55.         list->head = new_node;
  56.         list->tail = new_node;
  57.         list->size++;
  58.         return true;
  59.     }
  60.     // 链表尾插之前不为空
  61.     list->tail->next = new_node;
  62.     list->tail = new_node;
  63.     list->size++;
  64.     return true;
  65. }
  66. // 按索引插入结点
  67. /*
  68.     对于链表来说,第一个结点的索引规定是0,那么链表的结点索引合法范围是[0, size-1]
  69.     分析:
  70.     1.参数校验,对错误的参数进行错误的处理
  71.     index在这个函数中,合法的取值范围是[0, size]
  72.     其中0表示头插, size表示尾插
  73.     2.处理特殊值,尾插和头插直接调用已实现的函数
  74.     3.在index合法,且不是头插尾插,即在中间插入结点时
  75. */
  76. bool add_by_idx(LinkedList *list, int index, DataType data) {
  77.     if (index < 0 || index > list->size) {
  78.         printf("Illegal Arguments: index = %d\n", index);
  79.         return false;
  80.     }
  81.     if (index == 0) {
  82.         return add_before_head(list, data);
  83.     }
  84.     if (index == list->size) {
  85.         return add_behind_tail(list, data);
  86.     }
  87.     // 处理中间插入结点的情况
  88.     Node *new_node = malloc(sizeof(Node));
  89.     if (new_node == NULL) {
  90.         printf("error: malloc failed in add_index.\n");
  91.         return false;
  92.     }
  93.     new_node->data = data;
  94.     // 遍历链表,找到index-1位置的结点
  95.     Node *prev = list->head;
  96.     for (int i = 0; i < index - 1; i++) {
  97.         prev = prev->next;
  98.     }   // for循环结束时,prev指针指向index索引的前驱结点
  99.     // 让新结点指向prev的后继结点
  100.     new_node->next = prev->next;
  101.     // 让prev指向新结点
  102.     prev->next = new_node;
  103.     list->size++;
  104.     return true;
  105. }
  106. // 根据索引搜索结点
  107. Node *search_by_idx(LinkedList *list, int index) {
  108.     if (index < 0 || index > list->size - 1) {  // size-1就是最后一个结点
  109.         printf("Illegal Arguments: index = %d\n", index);
  110.         return NULL;
  111.     }
  112.     // 索引合法
  113.     Node *curr = list->head;
  114.     for (size_t i = 0; i < index; i++)  // 小于index就表示寻找index索引的结点
  115.     {
  116.         curr = curr->next;
  117.     }   // for循环结束时,curr指针指向index索引位置的结点
  118.     return curr;
  119. }
  120. // 根据数据搜索结点
  121. Node *search_by_data(LinkedList *list, DataType data) {
  122.     // 惯用法遍历每一个结点,遍历过程中判断即可
  123.     Node *curr = list->head;
  124.     while (curr != NULL) {
  125.         if (curr->data == data) {
  126.             // 找到了
  127.             return curr;
  128.         }
  129.         curr = curr->next;
  130.     }
  131.     // 遍历结束都没有return 说明没有找到
  132.     return NULL;
  133. }
  134. // 根据数据删除结点
  135. /*
  136.     对于一条单链表来说, 在一个确定的结点的后面进行删除/新增操作, 它的时间复杂度是O(1)
  137.     链表的结点删除,哪些是O(1)的呢?
  138.     1.删除第一个结点是不是? 是,因为这个过程基本只需要改变头指针的指向就可以了
  139.     2.删除最后一个结点是不是呢? 不是, 因为删除最后一个结点,需要找到倒数第二个结点,这需要遍历数组,时间复杂度是O(n)
  140.     3.删除中间的结点是不是呢? 肯定不是,因为需要遍历找到这个结点
  141.     于是将单链表的删除分解成两个步骤:
  142.     1.假如删除的是第一个结点
  143.     2.如果删除的是第一个结点外的其它结点
  144. */
  145. bool delete_by_data(LinkedList *list, DataType data) {
  146.     // 校验: 如果链表为空,不删除了
  147.     if (list->head == NULL) {
  148.         printf("链表为空,无法删除!\n");
  149.         return false;
  150.     }
  151.     Node *curr = list->head;    // 该指针用于遍历整条单链表
  152.     // 1.处理删除的是第一个结点的情况
  153.     if (curr->data == data) {
  154.         list->head = curr->next;    // 这步操作不论链表是否只有一个结点 都是要做的
  155.         if (list->head == NULL) {
  156.             // 说明链表仅有一个结点,删除后,要同步更新尾指针
  157.             list->tail = NULL;
  158.         }
  159.         free(curr);
  160.         list->size--;
  161.         return true;
  162.     }
  163.     // 2.处理其他结点的删除情况
  164.     // 已经有一个指针,叫curr,它目前指向第一个结点
  165.     Node *prev = curr;
  166.     curr = curr->next;
  167.     while (curr != NULL) {  // 从第二个结点开始,遍历完整条单链表
  168.         if (curr->data == data) {
  169.             // 找到了待删除目标结点,curr指向它,此时prev指向它的前驱
  170.             prev->next = curr->next;
  171.             if (curr->next == NULL) {
  172.                 // 待删除结点是尾结点
  173.                 list->tail = prev;
  174.             }
  175.             free(curr);
  176.             list->size--;
  177.             return true;
  178.         }
  179.         prev = curr;
  180.         curr = curr->next;
  181.     } // while循环结束时,curr指针指向NULL,prev指向链表的最后一个结点
  182.     // while循环结束都没有return, 说明没找到目标data的结点,删除失败
  183.     return false;
  184. }
  185. // 根据索引删除结点
  186. bool delete_by_idx(LinkedList *list, int idx) {
  187.     if (list->head == NULL) {
  188.         printf("链表为空,无法删除!\n");
  189.         return false;
  190.     }
  191.     // 检查链表是否为空或索引是否越界
  192.     if (idx < 0 || idx >= list->size) {
  193.         printf("Illegal Arguments: index = %d\n", idx);
  194.         return false;
  195.     }
  196.     Node *curr = list->head;
  197.     Node *prev = NULL;
  198.     // 如果要删除的是第一个结点
  199.     if (idx == 0) {
  200.         list->head = curr->next;  // 更新头指针
  201.         // 若删除的结点是唯一的结点
  202.         if (list->size == 1) {
  203.             list->tail = NULL;  // 更新尾指针
  204.         }
  205.         free(curr);
  206.         list->size--;
  207.         return true;
  208.     }
  209.     // 如果删除的结点不是第一个结点
  210.     for (int i = 0; i < idx; i++) {
  211.         prev = curr;
  212.         curr = curr->next;
  213.     }
  214.     // 让前驱结点指向后继结点
  215.     prev->next = curr->next;
  216.     // 如果要删除的是尾结点, 则需要更新尾指针
  217.     if (idx == list->size - 1) {
  218.         list->tail = prev;
  219.     }
  220.     free(curr);
  221.     list->size--;
  222.     return true;
  223. }
  224. // 打印单链表 格式为:1 -> 2 -> 3 ->...
  225. void print_list(LinkedList *list) {
  226.     Node *curr = list->head;
  227.     // 遍历此单链表
  228.     while (curr != NULL) {
  229.         printf("%d", curr->data);
  230.         // 如果不是链表的最后一个结点,就打印箭头
  231.         if (curr->next != NULL) {
  232.             printf(" -> ");
  233.         }
  234.         curr = curr->next;
  235.     }
  236.     printf("\n");
  237. }
  238.        
复制代码
  1. //
  2. // 实现尾插法构建链表
  3. void insert_tail(Node **head, int new_data) {
  4.     // 1.分配新结点,初始化它
  5.     Node *new_node = malloc(sizeof(Node));
  6.     if (new_node == NULL) {
  7.         printf("error: malloc failed in insert_tail.\n");
  8.         exit(1);
  9.     }
  10.     new_node->data = new_data;
  11.     new_node->next = NULL;
  12.     // 3.链表非空时,让原本最后一个结点指向新结点
  13.     if (*head != NULL) {
  14.         // 2.遍历找到最后一个结点
  15.         Node *end = *head;
  16.         while (end->next != NULL) {
  17.             end = end->next;
  18.         } // while循环结束时, end指向最后一个结点
  19.         end->next = new_node;
  20.         return;
  21.     }
  22.     // 链表尾插之前是空的,那就直接更新头指针就行了
  23.     *head = new_node;
  24. }
  25. ```c
  26. ```c
  27. 在这里插入代码片
复制代码
[code][/code]
办理方案总结:

   :

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

羊蹓狼

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表