当前位置:   article > 正文

【数据结构】顺序表与链表基础知识的超详细详解

顺序表与链表

目录

1、线性表

2、顺序表

3、链表

 4、顺序表和链表的区别


1、线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

 线性表:


2、顺序表

2.1概念及结构
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
1. 静态顺序表:使用定长数组存储元素。 
  1. #defint N 7
  2. typedef int SLDataType;
  3. typedef struct SeqList
  4. {
  5. SLDataType array[N];//定长数组
  6. size_t size; //有效数据个数
  7. }SeqList;

2. 动态顺序表:使用动态开辟的数组存储。

  1. typedef struct SeqList
  2. {
  3. SLDataType* array;//指向动态开辟的数组
  4. size_t size; //有效数据个数
  5. size_t capacity; //容量空间大小
  6. }SeqList;

3、链表

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

  •  从上图中可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续
  • 现实中的节点一般都是从堆上申请出来的
  • 堆上申请的空间,是按照一定策略来分配的,两次申请的空间可能连续,也可能不连续

 3.2 链表的分类

1. 单向或者双向
2. 带头或者不带头
3. 循环或者非循环
一共有八种链表结构, 虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:
1. 无头单向非循环链表: 结构简单 ,一般不会单独用来存数据。实际中更多是作为 其他数 结构的子结构 ,如哈希桶、图的邻接表等等。另外这种结构在 笔试面试 中出现很多。
2. 带头双向循环链表: 结构最复杂 ,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了.
下面是对单链表的实现。
  1. //无头,单项,非循环链表
  2. typedef int SLTDateType;
  3. typedef struct SListNode
  4. {
  5. SLTDateType data;
  6. struct SListNode* next;
  7. }SListNode;
  8. // 动态申请一个结点
  9. SListNode* BuySListNode(SLTDateType x)
  10. {
  11. SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
  12. newnode->data = x;
  13. newnode->next = NULL;
  14. return newnode;
  15. }
  16. // 单链表打印
  17. void SListPrint(SListNode* plist)
  18. {
  19. while (plist)
  20. {
  21. printf("%d ", plist->data);
  22. plist = plist->next;
  23. }
  24. }
  25. // 单链表尾插
  26. void SListPushBack(SListNode** pplist, SLTDateType x)
  27. {
  28. SListNode* head = *pplist;
  29. SListNode* newnode= BuySListNode(x);
  30. if (!( *pplist))
  31. {
  32. (*pplist) = newnode;
  33. }
  34. else
  35. {
  36. while ((*pplist)->next)
  37. {
  38. *pplist = (*pplist)->next;
  39. }
  40. (*pplist)->next = newnode;
  41. }
  42. *pplist = head;
  43. }
  44. // 单链表的头插
  45. void SListPushFront(SListNode** pplist, SLTDateType x)
  46. {
  47. SListNode* newnode = BuySListNode(x);
  48. if (!(*pplist))
  49. {
  50. (*pplist) = newnode;
  51. }
  52. else
  53. {
  54. SListNode* head = newnode;
  55. newnode->next = (*pplist);
  56. (*pplist) = head;
  57. }
  58. }
  59. // 单链表的尾删
  60. void SListPopBack(SListNode** pplist)
  61. {
  62. SListNode* head = *pplist;
  63. assert(*pplist);
  64. while ((*pplist)->next->next)
  65. {
  66. *pplist = (*pplist)->next;
  67. }
  68. free((*pplist)->next);
  69. (*pplist)->next = NULL;
  70. (*pplist) = head;
  71. }
  72. // 单链表头删
  73. void SListPopFront(SListNode** pplist)
  74. {
  75. SListNode* head = (*pplist)->next;
  76. free((*pplist));
  77. (*pplist) = NULL;
  78. (*pplist) = head;
  79. }


 4、顺序表和链表的区别

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持O(N)
任意位置插入或删除可能需要搬移元素,效率低只需要修改指针指向
插入动态顺序表,空间不够需要扩容没有容量的概念
应用场景元素高校存储+频繁访问任意位置插入和删除频繁
缓存利用率

最后是双向带头链表增删查改的实现 

  1. ListNode* ListCreate(ListNode* plist)
  2. {
  3. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  4. newnode->data = -1;
  5. newnode->next = newnode->prev = NULL;
  6. return newnode;
  7. }
  8. // 双向链表销毁
  9. void ListDestory(ListNode* plist)
  10. {
  11. while (plist)
  12. {
  13. ListNode* next = plist->next;
  14. plist->data=0;
  15. free(plist);
  16. plist = NULL;
  17. plist = next;
  18. }
  19. }
  20. // 双向链表打印
  21. void ListPrint(ListNode* plist)
  22. {
  23. ListNode* next = plist->next;
  24. while (next != plist)
  25. {
  26. printf("%d ", next->data);
  27. next = next->next;
  28. }
  29. printf("\n");
  30. }
  31. // 双向链表尾插
  32. void ListPushBack(ListNode* plist, LTDataType x)
  33. {
  34. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  35. newnode->next = plist;
  36. newnode->prev = plist->prev;
  37. plist->prev->next = newnode;
  38. plist->prev = newnode;
  39. }
  40. // 双向链表尾删
  41. void ListPopBack(ListNode* plist)
  42. {
  43. ListNode* tail = plist->prev;
  44. plist->prev = tail->prev;
  45. tail->prev->next = plist;
  46. free(tail);
  47. }
  48. // 双向链表头插
  49. void ListPushFront(ListNode* plist, LTDataType x)
  50. {
  51. ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
  52. newnode->next = plist->next;
  53. newnode->prev = plist;
  54. plist->next->prev = newnode;
  55. plist->next = newnode;
  56. }
  57. // 双向链表头删
  58. void ListPopFront(ListNode* plist)
  59. {
  60. ListNode* head = plist->next;
  61. plist->next = head->next;
  62. head->next->prev = plist;
  63. free(head);
  64. }

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

闽ICP备14008679号