• 【数据结构】链表超全整理~



    目录

    1.链表

    1.1 链表的概念及结构

     1.2 链表的分类

    1.2.1. 单向或者双向

    1.2.2. 带头或者不带头(是否有哨兵位

    1.2.3. 循环或者非循环

    1.3单链表相关函数代码

    1.4双向带头循环链表相关函数代码

    4.顺序表和链表的区别

    5.存储器相关知识:



    1.链表

    1.1 链表的概念及结构

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


     1.2 链表的分类

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


    1.2.1. 单向或者双向

     

    1.2.2. 带头或者不带头(是否有哨兵位

    哨兵位

    哨兵位要malloc一个新节点,哨兵里面不要存具体的值。也不能存链表的长度,万一链表里面的数据类型是char,链表长度超过128,就会溢出。

    哨兵位的存在方便头插。

     

    1.2.3. 循环或者非循环

     

    最常用的链表还是单向无头不循环链表双向带头循环链表。

    1. 无头单向非循环链表:

    结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

    2. 带头双向循环链表:

    结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。

    1.3单链表相关函数代码

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. // slist.h
    6. typedef int SLTDateType;
    7. typedef struct SListNode
    8. {
    9. SLTDateType data;
    10. struct SListNode* next;
    11. }SListNode,*PSListNode;
    12. //SListNode* p;
    13. //PSListNode p;
    14. //struct SListNode p; 等价
    15. // 动态申请一个节点
    16. SListNode* BuySListNode(SLTDateType x);
    17. // 单链表打印
    18. void SListPrint(SListNode* plist);
    19. // 单链表尾插
    20. void SListPushBack(SListNode** pplist, SLTDateType x);
    21. // 单链表的头插
    22. void SListPushFront(SListNode** pplist, SLTDateType x);
    23. // 单链表的尾删
    24. void SListPopBack(SListNode** pplist);
    25. // 单链表头删
    26. void SListPopFront(SListNode** pplist);
    27. // 单链表查找
    28. SListNode* SListFind(SListNode* plist, SLTDateType x);
    29. // 单链表在pos位置之后插入x
    30. // 分析思考为什么不在pos位置之前插入?
    31. void SListInsertAfter(SListNode* pos, SLTDateType x);
    32. // 单链表删除pos位置之后的值
    33. // 分析思考为什么不删除pos位置?
    34. void SListEraseAfter(SListNode* pos);
    35. // 单链表的销毁
    36. void SListDestroy(SListNode** pplist);
    1. #define _CRT_SECURE_NO_WARNINGS
    2. #include"slist.h"
    3. void SListPrint(SListNode* plist)
    4. {
    5. //不能断言,因为如果链表一个元素都没有,元素指向空,还是要打印
    6. SListNode* cur = plist;
    7. while (cur)
    8. {
    9. printf("%d->", cur->data);
    10. cur = cur->next;
    11. }
    12. printf("NULL\n");
    13. }
    14. SListNode* BuySListNode(SLTDateType x)
    15. {
    16. SListNode* p = (SListNode*)malloc(sizeof(SListNode));
    17. if (p == NULL)
    18. {
    19. printf("malloc fail\n");
    20. exit(-1);
    21. }
    22. p->data = x;
    23. p->next = NULL;
    24. return p;
    25. }
    26. void SListPushBack(SListNode** pplist, SLTDateType x)
    27. {
    28. SListNode* head = *pplist;
    29. SListNode* tail = BuySListNode(x);
    30. //空
    31. if (*pplist == NULL)
    32. {
    33. *pplist = tail;
    34. }
    35. //非空
    36. else
    37. {
    38. while (head->next)
    39. {
    40. head = head->next;
    41. }
    42. head->next = tail;
    43. }
    44. }
    45. void SListPushFront(SListNode** pplist, SLTDateType x)
    46. {
    47. assert(*pplist);
    48. SListNode* cur = *pplist;
    49. SListNode* new = BuySListNode(x);
    50. new->next = cur;
    51. *pplist = new;
    52. }
    53. void SListPopBack(SListNode** pplist)
    54. {
    55. assert(*pplist);
    56. SListNode* cur = *pplist;
    57. //一个结点
    58. if (cur->next == NULL)
    59. {
    60. free(cur);
    61. *pplist = NULL;
    62. }
    63. //多个节点
    64. else
    65. {
    66. while (cur->next->next)
    67. {
    68. cur = cur->next;
    69. }
    70. free(cur->next);
    71. cur->next = NULL;
    72. }
    73. }
    74. void SListPopFront(SListNode** pplist)
    75. {
    76. assert(*pplist);
    77. SListNode* cur = *pplist;
    78. *pplist = cur->next;
    79. free(cur);
    80. cur = NULL;
    81. }
    82. SListNode* SListFind(SListNode* plist, SLTDateType x)
    83. {
    84. assert(plist);
    85. SListNode* cur = plist;
    86. while (cur)
    87. {
    88. if (cur->data == x)
    89. {
    90. break;
    91. }
    92. cur = cur->next;
    93. }
    94. return cur;
    95. }
    96. void SListInsertAfter(SListNode* pos, SLTDateType x)
    97. {
    98. SListNode* p = BuySListNode(x);
    99. p->next = pos->next;
    100. pos->next = p;
    101. }
    102. void SListEraseAfter(SListNode* pos)
    103. {
    104. SListNode* p = pos->next;
    105. pos->next = p->next;
    106. free(p);
    107. }
    108. void SListDestroy(SListNode** pplist)
    109. {
    110. assert(*pplist);
    111. SListNode* cur = *pplist;
    112. while (cur)
    113. {
    114. SListNode* next = cur->next;
    115. free(cur);
    116. cur = next;
    117. }
    118. *pplist = NULL;
    119. }

    1.4双向带头循环链表相关函数代码

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. // 带头+双向+循环链表增删查改实现
    6. typedef int LTDataType;
    7. typedef struct ListNode
    8. {
    9. LTDataType data;
    10. struct ListNode* next;
    11. struct ListNode* prev;
    12. }ListNode;
    13. // 创建返回链表的头结点.
    14. ListNode* ListCreate();
    15. ListNode* AddListnode(LTDataType x);
    16. // 双向链表销毁
    17. void ListDestory(ListNode* pHead);
    18. // 双向链表打印
    19. void ListPrint(ListNode* pHead);
    20. // 双向链表尾插
    21. void ListPushBack(ListNode* pHead, LTDataType x);
    22. // 双向链表尾删
    23. void ListPopBack(ListNode* pHead);
    24. // 双向链表头插
    25. void ListPushFront(ListNode* pHead, LTDataType x);
    26. // 双向链表头删
    27. void ListPopFront(ListNode* pHead);
    28. // 双向链表查找
    29. ListNode* ListFind(ListNode* pHead, LTDataType x);
    30. // 双向链表在pos的前面进行插入
    31. void ListInsert(ListNode* pos, LTDataType x);
    32. // 双向链表删除pos位置的节点
    33. void ListErase(ListNode* pos);
    1. #define _CRT_SECURE_NO_WARNINGS
    2. #include"DLClinklist.h"
    3. // 创建返回链表的头结点.
    4. // 哨兵位
    5. ListNode* ListCreate()
    6. {
    7. ListNode* head = (ListNode*)malloc(sizeof(ListNode));
    8. if (head == NULL)
    9. {
    10. perror("malloc fail");
    11. exit(-1);
    12. }
    13. head->data = 0;
    14. head->next = head->prev = head;
    15. return head;
    16. }
    17. //创建节点
    18. ListNode* AddListnode(LTDataType x)
    19. {
    20. ListNode* p = (ListNode*)malloc(sizeof(ListNode));
    21. if (p == NULL)
    22. {
    23. perror("malloc fail");
    24. exit(-1);
    25. }
    26. p->data = x;
    27. p->next = p->prev = NULL;
    28. return p;
    29. }
    30. // 双向链表销毁
    31. void ListDestory(ListNode* pHead)
    32. {
    33. assert(pHead);
    34. ListNode* cur = pHead;
    35. while (cur)
    36. {
    37. cur = cur->next;
    38. free(pHead);
    39. pHead = cur;
    40. }
    41. }
    42. // 双向链表打印
    43. void ListPrint(ListNode* pHead)
    44. {
    45. assert(pHead);
    46. ListNode* cur = pHead->next;
    47. while (cur != pHead)
    48. {
    49. printf("%d->", cur->data);
    50. cur = cur->next;
    51. }
    52. printf("NULL\n");
    53. }
    54. // 双向链表尾插
    55. void ListPushBack(ListNode* pHead, LTDataType x)
    56. {
    57. assert(pHead);
    58. ListNode* newnode = AddListnode(x);
    59. newnode->next = pHead;
    60. newnode->prev = pHead->prev;
    61. pHead->prev->next = newnode;
    62. pHead->prev = newnode;
    63. }
    64. // 双向链表尾删
    65. void ListPopBack(ListNode* pHead)
    66. {
    67. assert(pHead);
    68. ListNode* tail = pHead->prev;
    69. tail->prev->next = pHead;
    70. pHead->prev = tail->prev;
    71. free(tail);
    72. tail = NULL;
    73. }
    74. // 双向链表头插
    75. void ListPushFront(ListNode* pHead, LTDataType x)
    76. {
    77. assert(pHead);
    78. ListNode* newnode = AddListnode(x);
    79. newnode->prev = pHead;
    80. newnode->next = pHead->next;
    81. pHead->next->prev = newnode;
    82. pHead->next = newnode;
    83. }
    84. // 双向链表头删
    85. void ListPopFront(ListNode* pHead)
    86. {
    87. assert(pHead);
    88. ListNode* next = pHead->next->next;
    89. free(pHead->next);
    90. pHead->next = next;
    91. next->prev = pHead;
    92. }
    93. // 双向链表查找
    94. ListNode* ListFind(ListNode* pHead, LTDataType x)
    95. {
    96. assert(pHead);
    97. ListNode* cur = pHead->next;
    98. while (cur != pHead)
    99. {
    100. if (cur->data == x)
    101. {
    102. return cur;
    103. }
    104. cur = cur->next;
    105. }
    106. return NULL;
    107. }
    108. // 双向链表在pos的前面进行插入
    109. void ListInsert(ListNode* pos, LTDataType x)
    110. {
    111. assert(pos);
    112. ListNode* ahead = pos->prev;
    113. ListNode* newnode = AddListnode(x);
    114. newnode->prev = ahead;
    115. newnode->next = pos;
    116. ahead->next = newnode;
    117. pos->prev = newnode;
    118. }
    119. // 双向链表删除pos位置的节点
    120. void ListErase(ListNode* pos)
    121. {
    122. assert(pos);
    123. ListNode* ahead = pos->prev;
    124. ListNode* next = pos->next;
    125. ahead->next = next;
    126. next->prev = ahead;
    127. free(pos);
    128. pos=NULL;
    129. }

    4.顺序表和链表的区别

    单纯的存储数据用的最多的就是顺序表和链表。

    比较时使用带头双向循环链表,单链表相比于顺序表的优势不大。

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

    存储效率而言实际上顺序表使用更多。

    双链表带头双向循环

    顺序表——————————————————

    优点:

    1.尾插尾删效率很高。

    2.顺序表可以随机访问(用下标可以直接访问)。

    3.相比链表结构CPU高速缓存的命中率更高。

    缺点:

    1.头部和中部的插入效率低,因为要挪动数据。

    2.扩容,扩容时单次的性能消耗(假如realloc异地扩容) +一定程度的空间浪费(每次扩容2倍可能用不完)。

    链表:——————————————————

    优点:

    1.任意位置插入删除,效率都很高--O(1)(在知道这个位置指针的前提下)

    2.按需申请释放空间,单次申请的效率要高一些,不存在空间浪费。

    缺点:

    1.不支持随机访问。


    5.存储器相关知识:


    CPU执行指令,不会直接访问内存:

    1.先看数据在不在三级缓存,在就叫(命中)。直接访问。

    2.不在(不命中),会让数据先加载到缓存,再访问。

    局部性原理:

    就是你访问当前这一块数据的时候,你很有可能访问周边的数据。就比如说你访问四个字节,它不会只加载这—段,会加载一长段字节。不同的硬件不同,

    是主存的一部分,堆是一个进程的虚拟地址空间
    堆、栈都是虚拟内存,需要经过操作系统对于物理的划分,后面会用链表跟物理地址进行映射。

    数据结构:
    在主存里面管理数据。
    缓存污染:

    缓存是有限的,在每次加载的时候因为局部性原理,加载一些无效的东西,会把一些没有用的数据换出去。


    你好,这里是媛仔。这篇博客真的攒了好久,终于要发布啦!!希望这篇博客对你能够有所帮助,也欢迎和我多多交流,共同进步~

  • 相关阅读:
    计算机竞赛 身份证识别系统 - 图像识别 深度学习
    租房业务全流程管理:Spring Boot系统应用
    金蝶云星空采购申请单计算最低价
    AOP概念及作用
    一文讲透java弱引用以及使用场景
    leetcode Top100(17)矩阵置零
    【Elasticsearch】Elasticsearch的分片和副本机制
    最刁钻的阿里面试官总结的面试者常用面试题,看看你会哪些?
    JAVA中一段有趣的代码-关于类、多态、变量的执行分析
    【Java 进阶篇】JavaScript 日期和时间详解
  • 原文地址:https://blog.csdn.net/vpurple_/article/details/126087479