当前位置:   article > 正文

数据结构 -- 链表详解_数据结构链表

数据结构链表

一、链表的概念及结构

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表的结构可以形象的看作是一辆火车,每块空间独立存在并且通过指针链接在一起。

在数据结构中:

注意:1、从上图可以看出,链式结构在逻辑上是连续的,但在物理上不一定连续。

           2、现实的节点一般都是从堆上申请出来的。

           3、在堆上申请的空间是按一定策略来分配的,两次申请的空间可能连续也可能不连续。

二、链表的分类

实际上链表的结构非常多样,以下情况组合起来就有八种链表结构

1、单向或者双向

2、带头(哨兵位)或者不带头

3、循环或者非循环

虽然链表中存在这么多结构,但是在实际中最常用的就只有两种结构:

1、无头单向非循环链表:结构简单,一般不会单独用来存储数据,实际中更多的是作为其他数据结构的子结构,如哈希桶、图的邻接表等。

2、带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表,这个结构虽然结构较为复杂,但是使用代码实现反而因为结构的原因更加简单了,这一点在后续代码实现过程中会体现出来。

三、链表的实现

在代码实现过程中,我们依然为两个链表分别建立三个文件

无头单向非循环链表:SList.h(函数声明)、SList.c(函数定义)、4.c(测试函数功能)

带头双向循环链表:List.h(函数声明)、List.c(函数定义)、5.c(测试函数功能)

3.1 无头单向非循环链表

1)创建结构体

首先我们需要创建一个结构体,其中一个存放数据,另一个指向下一个空间。

  1. typedef int SLTDateType;
  2. typedef struct SListNode
  3. {
  4. SLTDateType data;
  5. struct SListNode* next;
  6. }SListNode;

2)尾插/头插

尾插需要先判断链表是否为空,为空则直接建立一个节点就行,不为空则需要遍历到最后一个节点后插入

  1. // 单链表的尾插
  2. void SListPushBack(SListNode** pplist, SLTDateType x)
  3. {
  4. assert(pplist);
  5. assert(*pplist);
  6. SListNode* newnode = BuySListNode(x);
  7. if (*pplist == NULL)
  8. {
  9. *pplist = newnode;
  10. }
  11. else
  12. {
  13. SListNode* tail = *pplist;
  14. while (tail->next != NULL)
  15. {
  16. tail = tail->next;
  17. }
  18. tail->next = newnode;
  19. }
  20. }

头插的实现较为简单,只需要在链表头插入即可

  1. // 单链表的头插
  2. void SListPushFront(SListNode** pplist, SLTDateType x)
  3. {
  4. assert(pplist);
  5. SListNode* newnode = BuySListNode(x);
  6. newnode->next = *pplist;
  7. *pplist = newnode;
  8. }

测试结果

3)尾删/头删

尾删和尾插的思路刚好相反,注意需要判空

  1. // 单链表的尾删
  2. void SListPopBack(SListNode** pplist)
  3. {
  4. assert(pplist);
  5. assert(*pplist);
  6. if ((*pplist)->next == NULL)
  7. {
  8. free(*pplist);
  9. *pplist = NULL;
  10. }
  11. else
  12. {
  13. SListNode* tail = *pplist;
  14. while (tail->next->next)
  15. {
  16. tail = tail->next;
  17. }
  18. free(tail->next);
  19. tail->next = NULL;
  20. }
  21. }
  1. // 单链表的头删
  2. void SListPopFront(SListNode** pplist)
  3. {
  4. assert(pplist);
  5. assert(*pplist);
  6. if ((*pplist)->next == NULL)
  7. {
  8. free(*pplist);
  9. *pplist = NULL;
  10. }
  11. else
  12. {
  13. SListNode* del = *pplist;
  14. *pplist = (*pplist)->next;
  15. free(del);
  16. }
  17. }

测试结果

4)查找

  1. // 单链表查找
  2. SListNode* SListFind(SListNode* plist, SLTDateType x)
  3. {
  4. SListNode* cur = plist;
  5. while (cur)
  6. {
  7. if (cur->data == x)
  8. {
  9. return cur;
  10. }
  11. cur = cur->next;
  12. }
  13. return NULL;
  14. }

3.2 带头双向循环链表

1)创建结构体

这里我们依然创建一个结构体,其中一个存放数据,另外两个分别指向前一片空间和后一片空间

  1. typedef int LTDataType;
  2. typedef struct ListNode
  3. {
  4. LTDataType _data;
  5. struct ListNode* _next;
  6. struct ListNode* _prev;
  7. }ListNode;

2)创建头结点(哨兵位)

创建哨兵位时只有这一个元素,该元素的前后指针都指向自己,已到达循环的目的

  1. // 创建头结点(哨兵位)
  2. ListNode* ListCreate()
  3. {
  4. ListNode* pHead = BuyNode(-1);
  5. pHead->_next = pHead;
  6. pHead->_prev = pHead;
  7. return pHead;
  8. }

3)尾插/头插

  1. // 双链表的尾插
  2. void ListPushBack(ListNode* pHead, LTDataType x)
  3. {
  4. assert(pHead);
  5. ListNode* tail = pHead->_prev;
  6. ListNode* newnode = BuyNode(x);
  7. tail->_next = newnode;
  8. newnode->_prev = tail;
  9. newnode->_next = pHead;
  10. pHead->_prev = newnode;
  11. }
  1. // 双链表的头插
  2. void ListPushFront(ListNode* pHead, LTDataType x)
  3. {
  4. assert(pHead);
  5. ListNode* newnode = BuyNode(x);
  6. ListNode* first = pHead->_next;
  7. pHead->_next = newnode;
  8. newnode->_prev = pHead;
  9. newnode->_next = first;
  10. first->_prev = newnode;
  11. }

测试结果

4)尾删/头删

  1. // 双链表的尾删
  2. void ListPopBack(ListNode* pHead)
  3. {
  4. assert(pHead);
  5. ListNode* tail = pHead->_prev;
  6. ListNode* tailprev = tail->_prev;
  7. free(tail);
  8. tailprev->_next = pHead;
  9. pHead->_prev = tailprev;
  10. }
  1. // 双链表的头删
  2. void ListPopFront(ListNode* pHead)
  3. {
  4. assert(pHead);
  5. ListNode* first = pHead->_next;
  6. ListNode* second = first->_next;
  7. pHead->_next = second;
  8. second->_prev = pHead;
  9. free(first);
  10. }

测试结果

5)查找

  1. // 双链表的查找
  2. ListNode* ListFind(ListNode* pHead, LTDataType x)
  3. {
  4. assert(pHead);
  5. ListNode* cur = pHead->_next;
  6. while (cur != pHead)
  7. {
  8. if (cur->_data == x)
  9. {
  10. return cur;
  11. }
  12. cur = cur->_next;
  13. }
  14. return NULL;
  15. }

6)在指定位置前插入

  1. // 在pos位置的前面插入
  2. void ListInsert(ListNode* pos, LTDataType x)
  3. {
  4. assert(pos);
  5. ListNode* prev = pos->_prev;
  6. ListNode* newnode = BuyNode(x);
  7. prev->_next = newnode;
  8. newnode->_prev = prev;
  9. newnode->_next = pos;
  10. pos->_prev = newnode;
  11. }

7)删除指定位置的节点

  1. // 删除pos位置的节点
  2. void ListErase(ListNode* pos)
  3. {
  4. assert(pos);
  5. ListNode* posprev = pos->_prev;
  6. ListNode* posnext = pos->_next;
  7. posprev->_next = posnext;
  8. posnext->_prev = posprev;
  9. free(pos);
  10. }

四、源码

4.1 无头单向非循环链表

SList.h(函数声明)

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<assert.h>
  4. typedef int SLTDateType;
  5. typedef struct SListNode
  6. {
  7. SLTDateType data;
  8. struct SListNode* next;
  9. }SListNode;
  10. // 动态申请一个节点
  11. SListNode* BuySListNode(SLTDateType x);
  12. // 单链表打印
  13. void SListPrint(SListNode* plist);
  14. // 单链表尾插
  15. void SListPushBack(SListNode** pplist, SLTDateType x);
  16. // 单链表的头插
  17. void SListPushFront(SListNode** pplist, SLTDateType x);
  18. // 单链表的尾删
  19. void SListPopBack(SListNode** pplist);
  20. // 单链表头删
  21. void SListPopFront(SListNode** pplist);
  22. // 单链表查找
  23. SListNode* SListFind(SListNode* plist, SLTDateType x);

SList.c(函数定义)

  1. #include"SList.h"
  2. SListNode* BuySListNode(SLTDateType x)
  3. {
  4. SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
  5. if (newnode == NULL)
  6. {
  7. perror("malloc fail");
  8. return;
  9. }
  10. newnode->data = x;
  11. newnode->next = NULL;
  12. return newnode;
  13. }
  14. void SListPrint(SListNode* plist)
  15. {
  16. SListNode* cur = plist;
  17. while (cur != NULL)
  18. {
  19. printf("%d->", cur->data);
  20. cur = cur->next;
  21. }
  22. printf("NULL\n");
  23. }
  24. void SListPushBack(SListNode** pplist, SLTDateType x)
  25. {
  26. assert(pplist);
  27. assert(*pplist);
  28. SListNode* newnode = BuySListNode(x);
  29. if (*pplist == NULL)
  30. {
  31. *pplist = newnode;
  32. }
  33. else
  34. {
  35. SListNode* tail = *pplist;
  36. while (tail->next != NULL)
  37. {
  38. tail = tail->next;
  39. }
  40. tail->next = newnode;
  41. }
  42. }
  43. void SListPushFront(SListNode** pplist, SLTDateType x)
  44. {
  45. assert(pplist);
  46. SListNode* newnode = BuySListNode(x);
  47. newnode->next = *pplist;
  48. *pplist = newnode;
  49. }
  50. void SListPopBack(SListNode** pplist)
  51. {
  52. assert(pplist);
  53. assert(*pplist);
  54. if ((*pplist)->next == NULL)
  55. {
  56. free(*pplist);
  57. *pplist = NULL;
  58. }
  59. else
  60. {
  61. SListNode* tail = *pplist;
  62. while (tail->next->next)
  63. {
  64. tail = tail->next;
  65. }
  66. free(tail->next);
  67. tail->next = NULL;
  68. }
  69. }
  70. void SListPopFront(SListNode** pplist)
  71. {
  72. assert(pplist);
  73. assert(*pplist);
  74. if ((*pplist)->next == NULL)
  75. {
  76. free(*pplist);
  77. *pplist = NULL;
  78. }
  79. else
  80. {
  81. SListNode* del = *pplist;
  82. *pplist = (*pplist)->next;
  83. free(del);
  84. }
  85. }
  86. SListNode* SListFind(SListNode* plist, SLTDateType x)
  87. {
  88. SListNode* cur = plist;
  89. while (cur)
  90. {
  91. if (cur->data == x)
  92. {
  93. return cur;
  94. }
  95. cur = cur->next;
  96. }
  97. return NULL;
  98. }

4.c(测试函数功能)

  1. #include"SList.h"
  2. void TestSList1()
  3. {
  4. SListNode* plist = NULL;
  5. SListPushFront(&plist, 1);
  6. SListPushFront(&plist, 2);
  7. SListPushFront(&plist, 3);
  8. SListPushFront(&plist, 4);
  9. SListPrint(plist);
  10. SListPushBack(&plist, 5);
  11. SListPushBack(&plist, 6);
  12. SListPushBack(&plist, 7);
  13. SListPushBack(&plist, 8);
  14. SListPrint(plist);
  15. SListPopBack(&plist);
  16. SListPopBack(&plist);
  17. SListPopBack(&plist);
  18. SListPrint(plist);
  19. SListPopBack(&plist);
  20. SListPrint(plist);
  21. SListPopBack(&plist);
  22. SListPrint(plist);
  23. SListPopBack(&plist);
  24. SListPrint(plist);
  25. SListPopBack(&plist);
  26. SListPrint(plist);
  27. SListPopBack(&plist);
  28. SListPrint(plist);
  29. /*SListPopBack(&plist);
  30. SListPrint(plist);*/
  31. /*SListPopFront(&plist);
  32. SListPrint(plist);
  33. SListPopFront(&plist);
  34. SListPrint(plist);
  35. SListPopFront(&plist);
  36. SListPrint(plist);
  37. SListPopFront(&plist);
  38. SListPrint(plist);
  39. SListPopFront(&plist);
  40. SListPrint(plist);*/
  41. }
  42. int main()
  43. {
  44. TestSList1();
  45. return 0;
  46. }

4.2 带头双向循环链表

List.h(函数声明)

  1. #include<stdio.h>
  2. #include<assert.h>
  3. #include<stdlib.h>
  4. #include<stdbool.h>
  5. typedef int LTDataType;
  6. typedef struct ListNode
  7. {
  8. LTDataType _data;
  9. struct ListNode* _next;
  10. struct ListNode* _prev;
  11. }ListNode;
  12. // 创建返回链表的头结点.
  13. ListNode* ListCreate();
  14. // 双向链表销毁
  15. void ListDestory(ListNode* pHead);
  16. // 双向链表打印
  17. void ListPrint(ListNode* pHead);
  18. // 双向链表尾插
  19. void ListPushBack(ListNode* pHead, LTDataType x);
  20. // 双向链表尾删
  21. void ListPopBack(ListNode* pHead);
  22. // 双向链表头插
  23. void ListPushFront(ListNode* pHead, LTDataType x);
  24. // 双向链表头删
  25. void ListPopFront(ListNode* pHead);
  26. // 双向链表查找
  27. ListNode* ListFind(ListNode* pHead, LTDataType x);
  28. // 双向链表在pos的前面进行插入
  29. void ListInsert(ListNode* pos, LTDataType x);
  30. // 双向链表删除pos位置的节点
  31. void ListErase(ListNode* pos);

List.c(函数定义)

  1. #include"List.h"
  2. ListNode* BuyNode(LTDataType x)
  3. {
  4. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  5. if (newnode == NULL)
  6. {
  7. perror("malloc fail");
  8. return NULL;
  9. }
  10. newnode->_data = x;
  11. newnode->_next = NULL;
  12. newnode->_prev = NULL;
  13. }
  14. ListNode* ListCreate()
  15. {
  16. ListNode* pHead = BuyNode(-1);
  17. pHead->_next = pHead;
  18. pHead->_prev = pHead;
  19. return pHead;
  20. }
  21. void ListPrint(ListNode* pHead)
  22. {
  23. assert(pHead);
  24. printf("sentinel<==>");
  25. ListNode* cur = pHead->_next;
  26. while (cur != pHead)
  27. {
  28. printf("%d<==>", cur->_data);
  29. cur = cur->_next;
  30. }
  31. printf("\n");
  32. }
  33. bool LTEmpty(ListNode* pHead)
  34. {
  35. assert(pHead);
  36. return pHead->_next == pHead;
  37. }
  38. void ListPushBack(ListNode* pHead, LTDataType x)
  39. {
  40. assert(pHead);
  41. ListNode* tail = pHead->_prev;
  42. ListNode* newnode = BuyNode(x);
  43. tail->_next = newnode;
  44. newnode->_prev = tail;
  45. newnode->_next = pHead;
  46. pHead->_prev = newnode;
  47. }
  48. void ListPopBack(ListNode* pHead)
  49. {
  50. assert(pHead);
  51. ListNode* tail = pHead->_prev;
  52. ListNode* tailprev = tail->_prev;
  53. free(tail);
  54. tailprev->_next = pHead;
  55. pHead->_prev = tailprev;
  56. }
  57. void ListPushFront(ListNode* pHead, LTDataType x)
  58. {
  59. assert(pHead);
  60. ListNode* newnode = BuyNode(x);
  61. ListNode* first = pHead->_next;
  62. pHead->_next = newnode;
  63. newnode->_prev = pHead;
  64. newnode->_next = first;
  65. first->_prev = newnode;
  66. }
  67. void ListPopFront(ListNode* pHead)
  68. {
  69. assert(pHead);
  70. ListNode* first = pHead->_next;
  71. ListNode* second = first->_next;
  72. pHead->_next = second;
  73. second->_prev = pHead;
  74. free(first);
  75. }
  76. ListNode* ListFind(ListNode* pHead, LTDataType x)
  77. {
  78. assert(pHead);
  79. ListNode* cur = pHead->_next;
  80. while (cur != pHead)
  81. {
  82. if (cur->_data == x)
  83. {
  84. return cur;
  85. }
  86. cur = cur->_next;
  87. }
  88. return NULL;
  89. }
  90. void ListInsert(ListNode* pos, LTDataType x)
  91. {
  92. assert(pos);
  93. ListNode* prev = pos->_prev;
  94. ListNode* newnode = BuyNode(x);
  95. prev->_next = newnode;
  96. newnode->_prev = prev;
  97. newnode->_next = pos;
  98. pos->_prev = newnode;
  99. }
  100. void ListErase(ListNode* pos)
  101. {
  102. assert(pos);
  103. ListNode* posprev = pos->_prev;
  104. ListNode* posnext = pos->_next;
  105. posprev->_next = posnext;
  106. posnext->_prev = posprev;
  107. free(pos);
  108. }
  109. void ListDestory(ListNode* pHead)
  110. {
  111. assert(pHead);
  112. ListNode* cur = pHead->_next;
  113. while (cur != pHead)
  114. {
  115. ListNode* next = cur->_next;
  116. free(cur);
  117. cur = next;
  118. }
  119. free(pHead);
  120. }

5.c(测试函数功能)

  1. #include"List.h"
  2. TestList()
  3. {
  4. ListNode* plist = ListCreate();
  5. ListPushBack(plist, 1);
  6. ListPushBack(plist, 2);
  7. ListPushBack(plist, 3);
  8. ListPushBack(plist, 4);
  9. ListPrint(plist);
  10. ListPushFront(plist, 5);
  11. ListPushFront(plist, 6);
  12. ListPushFront(plist, 7);
  13. ListPushFront(plist, 8);
  14. ListPrint(plist);
  15. ListPopBack(plist);
  16. ListPopBack(plist);
  17. ListPopBack(plist);
  18. ListPrint(plist);
  19. ListPopFront(plist);
  20. ListPopFront(plist);
  21. ListPopFront(plist);
  22. ListPrint(plist);
  23. }
  24. int main()
  25. {
  26. TestList();
  27. return 0;
  28. }

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

闽ICP备14008679号