• <单链表及模拟实现>——《Data Structure in C Train》


    目录

    详细实现链接:

    <单链表(含头结点)>《数据结构(C语言版)》

    1.链表表示和实现(单链表+双向链表)

    1.1 链表的概念及结构

    1.2单链表的实现

    2.顺序表和链表的区别和联系

    后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知


    详细实现链接:

    <单链表(含头结点)>《数据结构(C语言版)》

    1.链表表示和实现(单链表+双向链表)

    顺序表的问题及思考
    问题:
    1. 中间/头部的插入删除,时间复杂度为O(N)
    2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
    3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。
    思考:
    如何解决以上问题呢?下面给出了链表的结构来看看。

    1.1 链表的概念及结构

    概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
    举例(这里采用单链表画图举例):

     理解cur=cur->next:

     理解tail !=NULL:

     理解tail->next !=NULL:

     对比发现两者最终结束的位置不同!

    实际中要实现的链表的结构非常多样,以下情况组合起来就有8种链表结构:
    1. 单向、双向
    2. 带头、不带头
    3. 循环、非循环

    虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:  

    1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
    2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

    1.2单链表的实现

    SList.h:

    1. #pragma once
    2. #include
    3. #include
    4. typedef int SLTDataType;
    5. struct SListNode
    6. {
    7. SLTDataType data;
    8. struct SListNode* next;
    9. };
    10. typedef struct SListNode SLTNode;
    11. // 不用改变链表的头指针,传一级指针
    12. //单链表打印
    13. void SListPrint(SLTNode* phead);
    14. // 可能会要改变链表的头指针,传二级指针
    15. //单链表尾插
    16. void SListPushBack(SLTNode** pphead, SLTDataType x);
    17. //单链表头插
    18. void SListPushFront(SLTNode** pphead, SLTDataType x);
    19. //单链表头删
    20. void SListPopFront(SLTNode** pphead);
    21. //单链表尾删
    22. void SListPopBack(SLTNode** pphead);
    23. //单链表按值查找
    24. SLTNode* SListFind(SLTNode* phead, SLTDataType x);
    25. // 在pos的前面插入x
    26. void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);
    27. // 删除pos位置的值
    28. void SListErase(SLTNode** phead, SLTNode* pos);
    29. // 有些地方也有这样的
    30. 在pos的前面插入x
    31. //void SListInsert(SLTNode** phead, int i, SLTDataType x);
    32. 删除pos位置的值
    33. //void SListErase(SLTNode** phead, int i);

    SList.c:

    1. #include "SList.h"
    2. //单链表打印
    3. void SListPrint(SLTNode* phead)
    4. {
    5. SLTNode* cur = phead;
    6. while (cur != NULL)
    7. {
    8. printf("%d->", cur->data);
    9. cur = cur->next; //通过"物理结构"更好理解,地址赋值,使得cur进行了"移动"
    10. }
    11. printf("NULL\n");
    12. }
    13. //封装函数:动态开辟新结点
    14. SLTNode* BuySListNode(SLTDataType x)
    15. {
    16. SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    17. newnode->data = x;
    18. newnode->next = NULL;
    19. return newnode;
    20. }
    21. //单链表尾插
    22. void SListPushBack(SLTNode** pphead, SLTDataType x)
    23. {
    24. SLTNode* newnode = BuySListNode(x);
    25. if (*pphead == NULL)
    26. {
    27. *pphead = newnode;
    28. }
    29. else
    30. {
    31. // 找尾节点的指针
    32. SLTNode* tail = *pphead;
    33. while (tail->next != NULL)
    34. {
    35. tail = tail->next;
    36. }
    37. // 尾节点,链接新节点
    38. tail->next = newnode;
    39. }
    40. }
    41. //单链表头插
    42. void SListPushFront(SLTNode** pphead, SLTDataType x)
    43. {
    44. SLTNode* newnode = BuySListNode(x);
    45. newnode->next = *pphead;
    46. *pphead = newnode;
    47. }
    48. //单链表头删
    49. void SListPopFront(SLTNode** pphead)
    50. {
    51. SLTNode* next = (*pphead)->next;
    52. free(*pphead);
    53. *pphead = next;
    54. }
    55. //单链表尾删
    56. void SListPopBack(SLTNode** pphead)
    57. {
    58. // 1、空
    59. // 2、一个节点
    60. // 3、一个以上的节点
    61. if (*pphead == NULL)
    62. {
    63. return;
    64. }
    65. else if ((*pphead)->next == NULL)
    66. {
    67. free(*pphead);
    68. *pphead = NULL;
    69. }
    70. else
    71. {
    72. SLTNode* prev = NULL;
    73. SLTNode* tail = *pphead;
    74. while (tail->next != NULL)
    75. {
    76. prev = tail;
    77. tail = tail->next;
    78. }
    79. free(tail);
    80. prev->next = NULL;
    81. }
    82. }
    83. //单链表按值查找
    84. SLTNode* SListFind(SLTNode* phead, SLTDataType x)
    85. {
    86. SLTNode* cur = phead;
    87. //while (cur != NULL)
    88. while (cur)
    89. {
    90. if (cur->data == x)
    91. {
    92. return cur;
    93. }
    94. cur = cur->next;
    95. }
    96. return NULL;
    97. }
    98. // 在pos的前面插入x
    99. void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
    100. {
    101. if (pos == *pphead)
    102. {
    103. SListPushFront(pphead, x);
    104. }
    105. else
    106. {
    107. SLTNode* newnode = BuySListNode(x);
    108. SLTNode* prev = *pphead;
    109. while (prev->next != pos)
    110. {
    111. prev = prev->next;
    112. }
    113. prev->next = newnode;
    114. newnode->next = pos;
    115. }
    116. }
    117. // 删除pos位置的值
    118. void SListErase(SLTNode** pphead, SLTNode* pos)
    119. {
    120. if (pos == *pphead)
    121. {
    122. SListPopFront(pphead);
    123. }
    124. else
    125. {
    126. SLTNode* prev = *pphead;
    127. while (prev->next != pos)
    128. {
    129. prev = prev->next;
    130. }
    131. prev->next = pos->next;
    132. free(pos);
    133. }
    134. }

    Test.c:

    1. #include "SList.h"
    2. void TestSList1()
    3. {
    4. SLTNode* plist = NULL;
    5. SListPushBack(&plist, 1);
    6. SListPushBack(&plist, 2);
    7. SListPushBack(&plist, 3);
    8. SListPushBack(&plist, 4);
    9. SListPrint(plist);
    10. SListPushFront(&plist, 0);
    11. SListPrint(plist);
    12. SListPopFront(&plist);
    13. SListPopFront(&plist);
    14. SListPopFront(&plist);
    15. SListPrint(plist);
    16. SListPopFront(&plist);
    17. SListPopFront(&plist);
    18. SListPrint(plist);
    19. }
    20. void TestSList2()
    21. {
    22. SLTNode* plist = NULL;
    23. SListPushBack(&plist, 1);
    24. SListPushBack(&plist, 2);
    25. SListPushBack(&plist, 3);
    26. SListPushBack(&plist, 4);
    27. SListPrint(plist);
    28. SListPopBack(&plist);
    29. SListPopBack(&plist);
    30. SListPopBack(&plist);
    31. SListPopBack(&plist);
    32. SListPopBack(&plist);
    33. SListPrint(plist);
    34. }
    35. void TestSList3()
    36. {
    37. SLTNode* plist = NULL;
    38. SListPushBack(&plist, 1);
    39. SListPushBack(&plist, 2);
    40. SListPushBack(&plist, 3);
    41. SListPushBack(&plist, 4);
    42. SListPrint(plist);
    43. //在3的前面插入一个30
    44. SLTNode* pos = SListFind(plist, 3);
    45. if (pos)
    46. {
    47. SListInsert(&plist, pos, 30);
    48. }
    49. SListPrint(plist);
    50. //在1的前面插入10
    51. pos = SListFind(plist, 1);
    52. if (pos)
    53. {
    54. SListInsert(&plist, pos, 10);
    55. }
    56. SListPrint(plist);
    57. }
    58. void TestSList4()
    59. {
    60. SLTNode* plist = NULL;
    61. SListPushBack(&plist, 1);
    62. SListPushBack(&plist, 2);
    63. SListPushBack(&plist, 3);
    64. SListPushBack(&plist, 4);
    65. SListPrint(plist);
    66. SLTNode* pos = SListFind(plist, 1);
    67. if (pos)
    68. {
    69. SListErase(&plist, pos);
    70. }
    71. SListPrint(plist);
    72. pos = SListFind(plist, 4);
    73. if (pos)
    74. {
    75. SListErase(&plist, pos);
    76. }
    77. SListPrint(plist);
    78. pos = SListFind(plist, 3);
    79. if (pos)
    80. {
    81. SListErase(&plist, pos);
    82. }
    83. SListPrint(plist);
    84. pos = SListFind(plist, 2);
    85. if (pos)
    86. {
    87. SListErase(&plist, pos);
    88. }
    89. SListPrint(plist);
    90. }
    91. int main()
    92. {
    93. TestSList1();
    94. TestSList2();
    95. TestSList3();
    96. TestSList4();
    97. return 0;
    98. }

    1.3 双向链表的表示和实现

    2.顺序表和链表的区别和联系

    顺序表:
    优点:
    空间连续、支持随机访问
    缺点:
    1. 中间或前面部分的插入删除时间复杂度O(N)
    2. 2.增容的代价比较大。
    链表:
    缺点:
    以节点为单位存储,不支持随机访问
    优点:
    1. 任意位置插入删除时间复杂度为O(1)
    2. 没有增容消耗,按需申请节点空间,不用了直接释放。

    后记:
    ●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知

  • 相关阅读:
    零零信安-D&D数据泄露报警日报【第42期】
    外汇天眼:外汇走势图的三种图表,看外汇图表这三种就够了
    P14 JDBC 快速入门
    DuckDB学习-初识tpcds
    深入理解Python中的面向对象编程(OOP)【第129篇—Scikit-learn的入门】
    HTML制作一个介绍自己家乡的网站——贵阳,排版整洁,内容丰富,主题鲜明
    【JAVA UI】HarmonyOS怎么判断Service怎么在后台运行
    vue提取字符串中中文汉字的大写首字母
    games101透视投影矩阵推导
    《计算机操作系统-第二章》之操作系统的运行机制与体系结构
  • 原文地址:https://blog.csdn.net/m0_57859086/article/details/126582804