当前位置:   article > 正文

【数据结构与算法】顺序表与链表(单链表和双链表)超详解图示与源码。_顺序表单链表双链表

顺序表单链表双链表

                                                  

大家好,今天我们来学习数据结构中的顺序表与链表!源码在最后附上

首先我们先来认识一下顺序表

                                     

**如上图所示:很多人会以为数组就是顺序表,顺序表就是数组,这种理解时错误的。

数组:

数组是相同数据类型的元素按一定顺序排列的的集合。数组中的元素存储在一个连续性的内存块中,并通过索引来访问。

简单的说,数组是在物理空间中连续存储的相同数据类型的元素的集合

而顺序表:

顺序表,全名顺序存储结构,是线性表的一种。(线性表(linear list)是数据结构的一种,一个线性表是 n 具有相同特性的数据元素的有限序列。线性表在逻辑上是线性结构,也就说是连续的一条直线,但是在物理结构上并不一定是连续的。常见的线性表:顺序表、链表、栈、队列、字符串等。

顺序表不仅要求数据在逻辑上是连续的一条直线,还要求用一段物理地址连续的存储单元以存储表中数据元素,一般情况下采用数组存储。

总结:

顺序表是在计算机内存中以数组的形式保存的线性表,只能从头开始连续存储。
此处将「数组」理解为物理结构,「顺序表」理解为逻辑结构比较合理
我们可以用数组实现顺序表,但我们同样可以用数组实现二叉树、队列等结构,因此不能直接认为顺序表就是数组
在顺序表中我们可以定义表中元素个数、表空间大小等变量,在数组中是没有的

                     

首先我们来学习一下顺序表的静态存储

                                         

而顺序表的静态存储又存在明显的缺陷和不足

不知道需要给确定的多少个空间,N给小了不够用,N给大了会浪费。

那么如何解决这个问题呢?

在这里我们使用malloc来动态开辟空间,再使用realloc来进行空间的管理

顺序表的动态存储:

                                   

在这里我们要注意realloc扩容存在异地扩容原地扩容俩种情况;

注意地址,当你realloc的空间没有那么大时,这里就是原地扩容

                         

注意地址,当你realloc的空间很大的时候,就会异地扩容

          

这里先让我们简单的来实现顺序表的几个功能:(完整功能最后附上源码)

实现顺序表的头插,即往前边插进去数据:

 

实现顺序表中间插入数据

实现顺序表的删除

通过以上学习,我们大概了解了顺序表的一些基础知识,那么顺序表是否存在一些缺点呢?

顺序表的缺点

1.尾部插入数据还不错,但是头部和中间插入删除数据,效率低下,需要挪动数据。 

2.满了之后只能扩容,而扩容是有一定的消耗的,扩容一般是存在一定的空间浪费(假如空间100已经满了,扩容到200,只需要插入120个数据,那么就有80个浪费掉了)

3.一次扩的多了,可能浪费掉了;一次扩的少了,有需要频繁去扩容。

                                                      

接下来让我们来学习链表 

链表:链表采用链式存储结构,其内存空间不连续

而链表分为单链表双链表,我们先来学习单链表

          

                          

它是由一个一个结点结合在一起的,而每个节点的地址没有关联,都是随机的,东一个西一个。

注意单链表的最后一个结点指向NULL;

这里我们简单的定义一个单链表:

                                        

如何去访问链表里的各个结点呢:我们定义一个cur指针指向单链表的头节点,然后通过cur->next来依次访问剩下的结点,如果cur==NULL,说明链表访问完了。

接下来我们来实现单链表几个基本的功能:(完整源码结尾附上)

单链表的尾插

                           

这里需要额外注意:因为phead是plist的临时拷贝,使用要注意二级指针与一级指针的使用,当然不使用二级指针也是可以 ,也有不同的写法,这里这样写是让大家小心注意一下其中的大坑。

单链表的头插:

 

单链表的尾删:

                                        

                                                       

需要注意,这里需要区分一个结点多个结点的不同情况。

单链表的头删:

                            

这里需要注意:我们的思路是把头节点的指向原本头结点的下一个结点,然后还需要free掉原本的头节点,这里会出现一种很常见的错误

                             

先free掉tmp后,就没办法再进行头结点的链接,丢失了地址。

正确的写法:(大家注意区分)

                                

关于链表还有一个知识点,就是哨兵位

                           

哨兵位就是在头节点的前面给它再加一个结点,让它充当头节点,但它不存储具体的数据,只是更方便进行头插头删等操作,哨兵位的含金量不是很高,有时候方便用来刷OJ题,这里大家简单了解下,有兴趣的朋友自己敲敲代码试一下把。

单链表我们就学到这里,接下来我们学习一下双链表

这里我们先再来了解下链表有哪些分类:

        

我们注意到,单链表和双链表的区别是:单链表一个结点只能指向下一个结点,而双边表一个结点可以指向前一个结点也可以指向后一个结点。

循环链表的特殊之处则是尾结点直接指向了头节点,实现了一个循环。

接下来我们先来比较一下俩种链表:

我们主要学习一下带头双向循环链表: 

根据上面单链表的学习,我们简单写几个功能让加强大家对双向链表的理解:

带头双向循环链表的尾插:

                         

我们可以明显感受到,双向循环链表的数据处理只需要几个简单的指向就可以简单完成;

带头双向循环链表的尾删:

                                 

知识的分享就结束了,接下来附上顺序表与链表的源码:(因为内容较多,所以不是很全,但有具体的框架,大家可以根据自己的需要进行补全)

顺序表:

SeqList.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. typedef int SLDataType;
  6. typedef struct SeqList
  7. {
  8. SLDataType* a;
  9. int size; // 有效数据
  10. int capacity; // 空间容量
  11. }SL;
  12. void SLInit(SL* psl);
  13. void SLDestroy(SL* psl);
  14. void SLPrint(SL* psl);
  15. void SLCheckCapacity(SL* psl);
  16. // 头尾插入删除
  17. void SLPushBack(SL* psl, SLDataType x);
  18. void SLPushFront(SL* psl, SLDataType x);
  19. void SLPopBack(SL* psl);
  20. void SLPopFront(SL* psl);
  21. // 任意下标位置的插入删除
  22. void SLInsert(SL* psl, int pos, SLDataType x);
  23. void SLErase(SL* psl, int pos);

 SeqList.c

  1. #include"SeqList.h"
  2. void SLInit(SL* psl)
  3. {
  4. assert(psl);
  5. psl->a = NULL;
  6. psl->size = 0;
  7. psl->capacity = 0;
  8. }
  9. void SLDestroy(SL* psl)
  10. {
  11. assert(psl);
  12. if (psl->a != NULL)
  13. {
  14. free(psl->a);
  15. psl->a = NULL;
  16. psl->size = 0;
  17. psl->capacity = 0;
  18. }
  19. }
  20. void SLPrint(SL* psl)
  21. {
  22. assert(psl);
  23. for (int i = 0; i < psl->size; i++)
  24. {
  25. printf("%d ", psl->a[i]);
  26. }
  27. printf("\n");
  28. }
  29. void SLCheckCapacity(SL* psl)
  30. {
  31. assert(psl);
  32. if (psl->size == psl->capacity)
  33. {
  34. int newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
  35. SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType)*newCapacity);
  36. if (tmp == NULL)
  37. {
  38. perror("realloc fail");
  39. return;
  40. }
  41. psl->a = tmp;
  42. psl->capacity = newCapacity;
  43. }
  44. }
  45. // 10:37
  46. void SLPushBack(SL* psl, SLDataType x)
  47. {
  48. assert(psl);
  49. SLCheckCapacity(psl);
  50. psl->a[psl->size] = x;
  51. psl->size++;
  52. }
  53. void SLPushFront(SL* psl, SLDataType x)
  54. {
  55. assert(psl);
  56. SLCheckCapacity(psl);
  57. // 挪动数据
  58. int end = psl->size - 1;
  59. while (end >= 0)
  60. {
  61. psl->a[end + 1] = psl->a[end];
  62. --end;
  63. }
  64. psl->a[0] = x;
  65. psl->size++;
  66. }
  67. void SLPopBack(SL* psl)
  68. {
  69. assert(psl);
  70. // 空
  71. // 温柔的检查
  72. /*if (psl->size == 0)
  73. {
  74. return;
  75. }*/
  76. // 暴力检查
  77. assert(psl->size > 0);
  78. //psl->a[psl->size - 1] = -1;
  79. psl->size--;
  80. }
  81. // 10:47
  82. void SLPopFront(SL* psl)
  83. {
  84. assert(psl);
  85. // 暴力检查
  86. assert(psl->size > 0);
  87. int begin = 1;
  88. while (begin < psl->size)
  89. {
  90. psl->a[begin - 1] = psl->a[begin];
  91. ++begin;
  92. }
  93. psl->size--;
  94. }
  95. // 注意pos是下标
  96. // size是数据个数,看做下标的话,他是最后一个数据的下一个位置
  97. void SLInsert(SL* psl, int pos, SLDataType x)
  98. {
  99. assert(psl);
  100. assert(pos >= 0 && pos <= psl->size);
  101. SLCheckCapacity(psl);
  102. // 挪动数据
  103. int end = psl->size - 1;
  104. while (end >= pos)
  105. {
  106. psl->a[end + 1] = psl->a[end];
  107. --end;
  108. }
  109. psl->a[pos] = x;
  110. psl->size++;
  111. }
  112. void SLErase(SL* psl, int pos)
  113. {
  114. assert(psl);
  115. assert(pos >= 0 && pos < psl->size);
  116. // 挪动覆盖
  117. int begin = pos + 1;
  118. while (begin < psl->size)
  119. {
  120. psl->a[begin - 1] = psl->a[begin];
  121. ++begin;
  122. }
  123. psl->size--;
  124. }

Test.c:

  1. #include<stdio.h>
  2. #include"SeqList.h"
  3. void TestSL1()
  4. {
  5. SL sl;
  6. SLInit(&sl);
  7. SLPushBack(&sl, 1);
  8. SLPushBack(&sl, 2);
  9. SLPushBack(&sl, 3);
  10. SLPushBack(&sl, 4);
  11. SLPushBack(&sl, 5);
  12. SLPushBack(&sl, 6);
  13. SLPushBack(&sl, 7);
  14. SLPushBack(&sl, 8);
  15. SLPushBack(&sl, 9);
  16. SLPrint(&sl);
  17. SLPushFront(&sl, 10);
  18. SLPushFront(&sl, 20);
  19. SLPushFront(&sl, 30);
  20. SLPushFront(&sl, 40);
  21. SLPrint(&sl);
  22. SLDestroy(&sl);
  23. }
  24. void TestSL2()
  25. {
  26. SL sl;
  27. SLInit(&sl);
  28. SLPushBack(&sl, 1);
  29. SLPushBack(&sl, 2);
  30. SLPushBack(&sl, 3);
  31. SLPushBack(&sl, 4);
  32. SLPushBack(&sl, 5);
  33. SLPrint(&sl);
  34. SLPopBack(&sl);
  35. SLPrint(&sl);
  36. SLPopBack(&sl);
  37. SLPopBack(&sl);
  38. SLPopBack(&sl);
  39. SLPopBack(&sl);
  40. SLPrint(&sl);
  41. SLPushFront(&sl, 10);
  42. SLPushFront(&sl, 20);
  43. SLPushFront(&sl, 30);
  44. SLPushFront(&sl, 40);
  45. SLPrint(&sl);
  46. SLDestroy(&sl);
  47. }
  48. void TestSL3()
  49. {
  50. SL sl;
  51. SLInit(&sl);
  52. SLPushBack(&sl, 1);
  53. SLPushBack(&sl, 2);
  54. SLPushBack(&sl, 3);
  55. SLPushBack(&sl, 4);
  56. SLPushBack(&sl, 5);
  57. SLPrint(&sl);
  58. SLPopFront(&sl);
  59. SLPrint(&sl);
  60. SLPopFront(&sl);
  61. SLPrint(&sl);
  62. SLPopFront(&sl);
  63. SLPrint(&sl);
  64. SLPopFront(&sl);
  65. SLPrint(&sl);
  66. SLPopFront(&sl);
  67. SLPrint(&sl);
  68. //SLPopFront(&sl);
  69. //SLPrint(&sl);
  70. }
  71. void TestSL4()
  72. {
  73. SL sl;
  74. SLInit(&sl);
  75. SLPushBack(&sl, 1);
  76. SLPushBack(&sl, 2);
  77. SLPushBack(&sl, 3);
  78. SLPushBack(&sl, 4);
  79. SLPushBack(&sl, 5);
  80. SLPrint(&sl);
  81. SLInsert(&sl, 2, 20);
  82. SLPrint(&sl);
  83. SLInsert(&sl, 6, 20);
  84. SLPrint(&sl);
  85. SLInsert(&sl, 0, 20);
  86. SLPrint(&sl);
  87. SLInsert(&sl, 10, 20);
  88. SLPrint(&sl);
  89. SLDestroy(&sl);
  90. }
  91. void TestSL5()
  92. {
  93. SL sl;
  94. SLInit(&sl);
  95. SLPushBack(&sl, 1);
  96. SLPushBack(&sl, 2);
  97. SLPushBack(&sl, 3);
  98. SLPushBack(&sl, 4);
  99. SLPushBack(&sl, 5);
  100. SLPrint(&sl);
  101. SLErase(&sl, 3);
  102. SLPrint(&sl);
  103. SLErase(&sl, 3);
  104. SLPrint(&sl);
  105. //SLErase(&sl, 3);
  106. //SLPrint(&sl);
  107. }
  108. int main()
  109. {
  110. TestSL5();
  111. return 0;
  112. }

单链表: 

 SList.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. typedef int SLNDataType;
  6. // Single List
  7. typedef struct SListNode
  8. {
  9. SLNDataType val;
  10. struct SListNode* next;
  11. }SLNode;
  12. void SLTPrint(SLNode* phead);
  13. void SLTPushBack(SLNode** pphead, SLNDataType x);
  14. void SLTPushFront(SLNode** pphead, SLNDataType x);
  15. void SLTPopBack(SLNode** pphead);
  16. void SLTPopFront(SLNode** pphead);
  17. SLNode* SLTFind(SLNode* phead, SLNDataType x);
  18. // posǰ
  19. SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);
  20. // ɾposλ
  21. void SLTErase(SLNode** pphead, SLNode* pos);
  22. void SLTDestroy(SLNode** pphead);

SList.c

  1. #include"SList.h"
  2. void SLTPrint(SLNode* phead)
  3. {
  4. SLNode* cur = phead;
  5. while (cur != NULL)
  6. {
  7. printf("%d->", cur->val);
  8. cur = cur->next;
  9. }
  10. printf("NULL\n");
  11. }
  12. SLNode* CreateNode(SLNDataType x)
  13. {
  14. SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
  15. if (newnode == NULL)
  16. {
  17. perror("malloc fail");
  18. exit(-1);
  19. }
  20. newnode->val = x;
  21. newnode->next = NULL;
  22. return newnode;
  23. }
  24. void SLTPushBack(SLNode** pphead, SLNDataType x)
  25. {
  26. SLNode* newnode = CreateNode(x);
  27. if (*pphead == NULL)
  28. {
  29. *pphead = newnode;
  30. }
  31. else
  32. {
  33. // 找尾
  34. SLNode* tail = *pphead;
  35. while (tail->next != NULL)
  36. {
  37. tail = tail->next;
  38. }
  39. tail->next = newnode;
  40. }
  41. }
  42. void SLTPushFront(SLNode** pphead, SLNDataType x)
  43. {
  44. SLNode* newnode = CreateNode(x);
  45. newnode->next = *pphead;
  46. *pphead = newnode;
  47. }
  48. void SLTPopBack(SLNode** pphead)
  49. {
  50. // 温柔的检查
  51. //if (*pphead == NULL)
  52. // return;
  53. // 空
  54. assert(*pphead);
  55. // 1、一个节点
  56. // 2、一个以上节点
  57. if ((*pphead)->next == NULL)
  58. {
  59. free(*pphead);
  60. *pphead = NULL;
  61. }
  62. else
  63. {
  64. // 找尾
  65. /*SLNode* prev = NULL;
  66. SLNode* tail = *pphead;
  67. while (tail->next != NULL)
  68. {
  69. prev = tail;
  70. tail = tail->next;
  71. }
  72. free(tail);
  73. tail = NULL;
  74. prev->next = NULL;*/
  75. SLNode* tail = *pphead;
  76. while (tail->next->next != NULL)
  77. {
  78. tail = tail->next;
  79. }
  80. free(tail->next);
  81. tail->next = NULL;
  82. }
  83. }
  84. void SLTPopFront(SLNode** pphead)
  85. {
  86. assert(*pphead);
  87. SLNode* tmp = *pphead;
  88. free(tmp);
  89. *pphead = (*pphead)->next;
  90. }

Test.c

  1. #include"SList.h"
  2. void TestSLT1()
  3. {
  4. SLNode* plist = NULL;
  5. SLTPushBack(&plist, 1);
  6. SLTPushBack(&plist, 2);
  7. SLTPushBack(&plist, 3);
  8. SLTPushBack(&plist, 4);
  9. SLTPrint(plist);
  10. SLTPopBack(&plist);
  11. SLTPrint(plist);
  12. SLTPopBack(&plist);
  13. SLTPrint(plist);
  14. SLTPopBack(&plist);
  15. SLTPrint(plist);
  16. SLTPopBack(&plist);
  17. SLTPrint(plist);
  18. //SLTPopBack(&plist);
  19. //SLTPrint(plist);
  20. }
  21. void TestSLT2()
  22. {
  23. SLNode* plist = NULL;
  24. SLTPushFront(&plist, 1);
  25. SLTPushFront(&plist, 2);
  26. SLTPushFront(&plist, 3);
  27. SLTPushFront(&plist, 4);
  28. SLTPrint(plist);
  29. SLTPopFront(&plist);
  30. SLTPrint(plist);
  31. //SLNode* pos = SLTFind(plist, 3);
  32. //SLTInsert(&plist, pos, 30);
  33. }
  34. //int main()
  35. //{
  36. // TestSLT2();
  37. //
  38. // return 0;
  39. //}
  40. struct ListNode
  41. {
  42. struct ListNode* next;
  43. int val;
  44. };
  45. struct ListNode* removeElements(struct ListNode* head, int val)
  46. {
  47. struct ListNode* prev = NULL;
  48. struct ListNode* cur = head;
  49. //while(cur != NULL)
  50. while (cur)
  51. {
  52. if (cur->val == val)
  53. {
  54. struct ListNode* next = cur->next;
  55. free(cur);
  56. if (prev)
  57. prev->next = next;
  58. cur = next;
  59. }
  60. else
  61. {
  62. prev = cur;
  63. cur = cur->next;
  64. }
  65. }
  66. return head;
  67. }
  68. int main()
  69. {
  70. struct ListNode* n1 = (struct ListNode*)malloc(sizeof(struct ListNode));
  71. struct ListNode* n2 = (struct ListNode*)malloc(sizeof(struct ListNode));
  72. struct ListNode* n3 = (struct ListNode*)malloc(sizeof(struct ListNode));
  73. struct ListNode* n4 = (struct ListNode*)malloc(sizeof(struct ListNode));
  74. n1->val = 7;
  75. n2->val = 7;
  76. n3->val = 7;
  77. n4->val = 7;
  78. n1->next = n2;
  79. n2->next = n3;
  80. n3->next = n4;
  81. n4->next = NULL;
  82. struct ListNode* head = removeElements(n1, 7);
  83. return 0;
  84. }

 

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

闽ICP备14008679号