当前位置:   article > 正文

C语言之链表详解_c 链表

c 链表

目录

一、链表定义

二、链表分类

三、链表操作

四、单向链表

1.链表定义

2.插入操作

3.删除操作

4.修改操作

5.查找操作

五、双向链表

1.链表定义

2.插入操作

3.删除操作

4.修改操作

5.查找操作


一、链表定义

        链表是一种基本的数据结构,它由一系列节点组成,每个节点包含一个值和指向下一个节点的指针。链表的特点是可以动态添加和删除节点,而不需要预先知道数据的数量。与数组不同,链表中的节点不一定是连续的存储空间,因此可以有效地利用内存空间。

二、链表分类

链表可以分为三种类型:

  • 单向链表:每个节点只有一个指针指向下一个节点,最后一个节点的指针指向NULL。
  • 双向链表:每个节点有两个指针,分别指向上一个节点和下一个节点,可以在O(1)时间内实现向前和向后遍历。
  • 循环链表:在单向或双向链表的基础上,将最后一个节点的指针指向头节点,形成一个环。

三、链表操作

链表的操作包括插入、删除、查找、遍历等。

  • 插入操作:可以在链表头或尾插入节点,也可以在指定位置插入节点。
  • 删除操作:可以删除指定节点或按照值删除节点。
  • 查找操作:可以查找指定节点或按照值查找节点。
  • 遍历操作:可以遍历整个链表,输出每个节点的值或执行其他操作。

        链表的优点是可以动态添加和删除节点,因此非常适用于需要频繁插入和删除数据的场景。链表的缺点是访问操作的时间复杂度为O(n),而且需要额外的空间存储节点的指针,因此在需要频繁访问数据的场景中,效率可能不如数组。

        链表在计算机科学中有广泛的应用,例如操作系统中的进程链表、文件系统中的目录链表、图论中的邻接表等。在C语言中,链表常常用于实现动态内存分配、函数调用栈、多项式运算等问题。

四、单向链表

        单向链表是由若干个节点组成的数据结构,每个节点包含两个部分:数据域和指针域。数据域存储节点的数据,指针域存储下一个节点的地址。

413c6e745fc63630140e7364e5c3bcf4.jpeg

1.链表定义

单向链表定义如下:

  1. struct ListNode {
  2. int val;
  3. struct ListNode* next;
  4. };

2.插入操作

链表的插入操作可以在链表的头部、尾部或指定位置插入节点。具体实现如下:

  1. // 在头部插入节点
  2. struct ListNode* insertAtHead(struct ListNode* head, int val) {
  3.     struct ListNode* new_node = (struct ListNode*)malloc(sizeof(struct ListNode));
  4.     new_node->val = val;
  5.     new_node->next = head;
  6.     return new_node;
  7. }
  8. // 在尾部插入节点
  9. struct ListNode* insertAtTail(struct ListNode* head, int val) {
  10.     struct ListNode* new_node = (struct ListNode*)malloc(sizeof(struct ListNode));
  11.     new_node->val = val;
  12.     new_node->next = NULL;
  13.     if (head == NULL) {
  14.         return new_node;
  15.     }
  16.     struct ListNode* p = head;
  17.     while (p->next != NULL) {
  18.         p = p->next;
  19.     }
  20.     p->next = new_node;
  21.     return head;
  22. }
  23. // 在指定位置插入节点
  24. struct ListNode* insertAtIndex(struct ListNode* head, int index, int val) {
  25. int i = 0;
  26.     struct ListNode* new_node = (struct ListNode*)malloc(sizeof(struct ListNode));
  27.     new_node->val = val;
  28.     if (index == 0) {
  29.         new_node->next = head;
  30.         return new_node;
  31.     }
  32.     struct ListNode* p = head;
  33.     
  34.     while (i < index - 1 && p != NULL) {
  35.         p = p->next;
  36.         i++;
  37.     }
  38.     if (p == NULL) {
  39.         free(new_node);
  40.         return head;
  41.     }
  42.     new_node->next = p->next;
  43.     p->next = new_node;
  44.     return head;
  45. }

3.删除操作

链表的删除操作可以删除指定位置或指定值的节点。具体实现如下:

  1. // 删除指定位置的节点
  2. struct ListNode* deleteAtIndex(struct ListNode* head, int index) {
  3. int i = 0;
  4.    
  5.   if (head == NULL) {
  6.         return NULL;
  7.     }
  8.     if (index == 0) {
  9.         struct ListNode* temp = head;
  10.         head = head->next;
  11.         free(temp);
  12.         return head;
  13.     }
  14.     struct ListNode* p = head;
  15.     
  16.     while (i < index - 1 && p != NULL) {
  17.         p = p->next;
  18. i++;
  19. }
  20. if (p == NULL || p->next == NULL) {
  21. return head;
  22. }
  23. struct ListNode* temp = p->next;
  24. p->next = p->next->next;
  25. free(temp);
  26. return head;
  27. }
  28. // 删除指定值的节点
  29. struct ListNode* deleteNode(struct ListNode* head, int val) {
  30. struct ListNode* p = head;
  31. struct ListNode* prev = NULL;
  32. while (p != NULL && p->val != val) {
  33. prev = p;
  34. p = p->next;
  35. }
  36. if (p == NULL) {
  37. return head;
  38. }
  39. if (prev == NULL) {
  40. head = head->next;
  41. else {
  42. prev->next = p->next;
  43. }
  44. free(p);
  45. return head;
  46. }

4.修改操作

链表的修改操作可以修改指定位置或指定值的节点的值。具体实现如下:

  1. // 修改指定位置的节点的值
  2. struct ListNode* modifyAtIndex(struct ListNode* head, int index, int val) {
  3.     struct ListNode* p = head;
  4.     int i = 0;
  5.     while (i < index && p != NULL) {
  6.         p = p->next;
  7.         i++;
  8.     }
  9.     if (p == NULL) {
  10.         return head;
  11.     }
  12.     p->val = val;
  13.     return head;
  14. }
  15. // 修改指定值的节点的值
  16. struct ListNode* modifyNode(struct ListNode* head, int old_val, int new_val) {
  17.     struct ListNode* p = head;
  18.     while (p != NULL && p->val != old_val) {
  19.         p = p->next;
  20.     }
  21.     if (p == NULL) {
  22.         return head;
  23.     }
  24.     p->val = new_val;
  25.     return head;
  26. }

5.查找操作

链表的查找操作可以查找指定位置或指定值的节点。具体实现如下:

  1. // 查找指定位置的节点的值
  2. int getValAtIndex(struct ListNode* head, int index) {
  3.     struct ListNode* p = head;
  4.     int i = 0;
  5.     while (i < index && p != NULL) {
  6.         p = p->next;
  7.         i++;
  8.     }
  9.     if (p == NULL) {
  10.         return -1;
  11.     }
  12.     return p->val;
  13. }
  14. // 查找指定值的节点的位置
  15. int getIndexByVal(struct ListNode* head, int val) {
  16.     struct ListNode* p = head;
  17.     int i = 0;
  18.     while (p != NULL && p->val != val) {
  19.         p = p->next;
  20.         i++;
  21.     }
  22.     if (p == NULL) {
  23.         return -1;
  24.     }
  25.     return i;
  26. }

        以上是链表的基本操作,实现时需要注意空指针和越界等问题。

五、双向链表

        双向链表和单向链表的不同在于每个节点有两个指针,分别指向前驱节点和后继节点。双向链表的优点是可以实现双向遍历和O(1)的删除操作,缺点是每个节点需要额外的一个指针。

20200726124645552.png

 1.链表定义

双向链表定义如下:

  1. struct ListNode {
  2.     int val;
  3.     struct ListNode* prev;
  4.     struct ListNode* next;
  5. };

2.插入操作

双向链表的插入操作可以在指定位置前面或后面插入节点,具体实现如下:

  1. // 在指定位置前面插入节点
  2. struct ListNode* insertBefore(struct ListNode* head, int index, int val) {
  3.     struct ListNode* p = head;
  4.     int i = 0;
  5.     while (i < index && p != NULL) {
  6.         p = p->next;
  7.         i++;
  8.     }
  9.     if (p == NULL) {
  10.         return head;
  11.     }
  12.     struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
  13.     node->val = val;
  14.     node->prev = p->prev;
  15.     node->next = p;
  16.     if (p->prev != NULL) {
  17.         p->prev->next = node;
  18.     } else {
  19.         head = node;
  20.     }
  21.     p->prev = node;
  22.     return head;
  23. }
  24. // 在指定位置后面插入节点
  25. struct ListNode* insertAfter(struct ListNode* head, int index, int val) {
  26.     struct ListNode* p = head;
  27.     int i = 0;
  28.     while (i < index && p != NULL) {
  29.         p = p->next;
  30.         i++;
  31.     }
  32.     if (p == NULL) {
  33.         return head;
  34.     }
  35.     struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
  36.     node->val = val;
  37.     node->prev = p;
  38.     node->next = p->next;
  39.     if (p->next != NULL) {
  40.         p->next->prev = node;
  41.     }
  42.     p->next = node;
  43.     return head;
  44. }

3.删除操作

双向链表的删除操作可以删除指定位置或指定值的节点,具体实现如下:

  1. // 删除指定位置的节点
  2. struct ListNode* deleteAtIndex(struct ListNode* head, int index) {
  3.     struct ListNode* p = head;
  4.     int i = 0;
  5.     while (i < index && p != NULL) {
  6.         p = p->next;
  7.         i++;
  8.     }
  9.     if (p == NULL) {
  10.         return head;
  11.     }
  12.     if (p->prev != NULL) {
  13.         p->prev->next = p->next;
  14.     } else {
  15.         head = p->next;
  16.     }
  17.     if (p->next != NULL) {
  18.         p->next->prev = p->prev;
  19.     }
  20.     free(p);
  21.     return head;
  22. }
  23. // 删除指定值的节点
  24. struct ListNode* deleteNode(struct ListNode* head, int val) {
  25.     struct ListNode* p = head;
  26.     while (p != NULL && p->val != val) {
  27.         p = p->next;
  28.     }
  29.     if (p == NULL) {
  30.         return head;
  31.     }
  32.     if (p->prev != NULL) {
  33.         p->prev->next = p->next;
  34.     } else {
  35. head = p->next;
  36. }
  37. if (p->next != NULL) {
  38.      p->next->prev = p->prev;
  39. }
  40. free(p);
  41. return head;
  42. }

4.修改操作

双向链表的修改操作可以直接修改指定节点的值,具体实现如下:

  1. // 修改指定位置的节点值
  2. struct ListNode* updateAtIndex(struct ListNode* head, int index, int val) {
  3.     struct ListNode* p = head;
  4.     int i = 0;
  5.     while (i < index && p != NULL) {
  6.         p = p->next;
  7.         i++;
  8.     }
  9.     if (p == NULL) {
  10.         return head;
  11.     }
  12.     p->val = val;
  13.     return head;
  14. }

5.查找操作

双向链表的查找操作可以按位置或按值查找,具体实现如下:

  1. // 按位置查找节点
  2. struct ListNode* getNodeAtIndex(struct ListNode* head, int index) {
  3.     struct ListNode* p = head;
  4.     int i = 0;
  5.     while (i < index && p != NULL) {
  6.         p = p->next;
  7.         i++;
  8.     }
  9.     return p;
  10. }
  11. // 按值查找节点
  12. struct ListNode* getNodeByValue(struct ListNode* head, int val) {
  13.     struct ListNode* p = head;
  14.     while (p != NULL && p->val != val) {
  15.         p = p->next;
  16.     }
  17.     return p;
  18. }

以上是双向链表的增删改查操作的实现,可以根据需要进行调用。需要注意的是,在使用完链表后要及时释放内存,避免内存泄漏。

        如果觉得文章写的还不错,麻烦点赞,收藏加关注哦​!


        关于更多嵌入式C语言、FreeRTOS、RT-Thread、Linux应用编程、linux驱动等相关知识,关注公众号【嵌入式Linux知识共享】,后续精彩内容及时收看了解。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/806954
推荐阅读
相关标签
  

闽ICP备14008679号