• 【数据结构初阶】单链表SLlist


    描述

    不同于顺序表顺序表的数据是存储在一个连续的空间里的

    链表它是链接起来的结构体地址。

    所以我们不用像顺序表一样先创建一块空间出来,而是创建一个能存数据节点和节点与下一个节点之间的连接

    所以:“一个能存数据节点”我们用int Data表示;

    与下一个节点之间的连接”:我们用指针连接起来。

    组织

    打印

    为了方便测试,我们先写个打印单链表的函数;

    1.这个打印函数需要断言吗?
    不需要,即使结构体为空,也能打印,只不过是没有数据而已,这时打印出来的就是空的。如果能打印出来,但是却断言报错,不太合适。

    2.怎么打印?
    用一个指针cur指向结构体,用while循环打印出来,当cur指向的结构体为空时,停止打印。

    3.while的判断条件可以是while(cur->next)吗?
    不可以,因为这样最后一个的数据就打印不出来了。

    4.在while循环中,让cur指向下一个结构体,可以用cur++吗?
    不可以,不同于顺序表,顺序表的数据是存储在一个连续的空间里的,链表它是链接起来的结构体地址,是节点与节点之间的连接,cur++无法指向下一个节点。
     

    1. void SLTPrint(SLTNode* phead)
    2. {
    3. SLTNode* cur = phead;
    4. while (cur)
    5. {
    6. printf("%d->", cur->Data);
    7. cur = cur->next;
    8. }
    9. printf("NULL\n");
    10. }

    1.尾插

    每在插入一个数据之前,首先得为这个结构体开辟一个结点

    用malloc开辟,由于插入数据时我们都要进行开辟一个结点的操作,所以我们可以打包成一个函数


    进行尾插首先就是要找到尾节点

    找尾分两种情况:

    1. 当*pphead本身为空时,就直接将newnode插入就可以了;

    2. *pphead本身不为空时,只要找到tail->next为空的,那个就是结构体的尾了


    当pphead不为空时,找尾while循环的判断条件可以写成这样tail!=NULL与插入结点时tail=newnode吗?
    不可以,因为这样就无法保持链接状态

    尾插的本质是:原为节点中存储新的尾节点的地址

    1. SLTNode* SLTNewnode(SLTDataType x)
    2. {
    3. SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    4. if (newnode == NULL)
    5. {
    6. perror("melloc fail");
    7. return NULL;
    8. }
    9. newnode->Data = x;
    10. newnode->next = NULL;
    11. return newnode;
    12. }
    13. void SLTPushBack(SLTNode** pphead, SLTDataType x)
    14. {
    15. //创建节点
    16. SLTNode* newnode = SLTNewnode(x);
    17. if (newnode == NULL)
    18. {
    19. return;
    20. }
    21. //情况一:pphead为空
    22. if (*pphead == NULL)
    23. {
    24. *pphead = newnode;
    25. }
    26. //情况二:pphead不为空
    27. else
    28. {
    29. //找到尾节点
    30. SLTNode* tail = *pphead;
    31. while (tail->next)
    32. {
    33. tail = tail->next;
    34. }
    35. tail->next = newnode;
    36. }
    37. }

    2.尾删

    1.尾删需要断言吗?
    需要,因为如果链表为空,是删不了的;


    2.尾删的思路
    尾删分三种讨论:

    1.如果链表为空,删不了,我们这里断言判断一下
    2.链表中只有一个数据
    3.链表中的数据为一个以上

    1. void SLTPopBack(SLTNode** pphead)
    2. {
    3. assert(*pphead);
    4. //只有一个节点
    5. if ((*pphead)->next == NULL)
    6. {
    7. free(*pphead);
    8. *pphead = NULL;
    9. }
    10. else
    11. {
    12. //找尾置空
    13. SLTNode* tail = *pphead;
    14. while (tail->next->next)
    15. {
    16. tail = tail->next;
    17. }
    18. free(tail->next);
    19. tail->next = NULL;
    20. }
    21. }

    3.头插

    1.头插需要断言吗?
    但是当链表为空的时候,可以插入数据,*pphead是不需要断言的。


    2.头插的思路
    首先先要创建一个结点,将结点的next与链表的第一个指针链接起来。最后要注意把链表的头给改成newnode。

    1. void SLTPushFront(SLTNode** pphead, SLTDataType x)
    2. {
    3. SLTNode* newnode = SLTNewnode(x);
    4. if (newnode == NULL)
    5. {
    6. return;
    7. }
    8. newnode->next = *pphead;
    9. *pphead = newnode;
    10. }


    4.头删

    1.头删需要断言吗?
    空链表不能删除,所以*pphead也需要断言。

    头删的思路:

    1. void SLTPopFront(SLTNode** pphead)
    2. {
    3. assert(*pphead);
    4. SLTNode* head = *pphead;
    5. *pphead = head->next;
    6. free(head);
    7. head = NULL;
    8. }


    5.查找

    1.查找需要断言吗?
    不需要,链表为空就返回NULL;


    2.查找的思路
    当链表的cur不为空,就继续逐一比对,找到了就返回cur,没找到就指向下一个,直到cur指向空;

    如果还没找到,就返回NULL;

    这里的phead用一级指针就可以了,因为不用修改里面的任何值;

    1. SLTNode* SListFind(SLTNode* phead, SLTDataType x)
    2. {
    3. SLTNode* cur = phead;
    4. while (cur)
    5. {
    6. if (cur->Data == x)
    7. {
    8. return cur;
    9. }
    10. cur = cur->next;
    11. }
    12. //如果没找到就返回NULL
    13. return NULL;
    14. }



    6.指定位置后插入

    1.需要断言吗?
    指定的位置pos不能为空,所以需要断言;


    2.思路
    创建一个新结点newnode,然后先让newnode->next = pos->next;让newnode的后面链接起来,在让pos和newnode链接起来pos->next = newnode;;

    反过来写的话,由于pos->next已经被改了,所以不能是pos与newnode链接,插入就会失败;

    1. void SLTInsertAfter( SLTNode* pos, SLTDataType x)
    2. {
    3. assert(pos);
    4. SLTNode* newnode = SLTNewnode(x);
    5. if (newnode == NULL)
    6. {
    7. return;
    8. }
    9. newnode->next = pos->next;
    10. pos->next = newnode;
    11. }



    7.指定位置后删除

    1.需要断言吗?
    指定的位置pos不能为空,所以需要断言;

    1. void SListEraseAfter( SLTNode* pos)
    2. {
    3. assert(pos);
    4. if (pos->next == NULL)
    5. {
    6. printf("已是最后一个,不能删\n");
    7. return;
    8. }
    9. SLTNode* cur = pos->next;
    10. pos->next = pos->next->next;
    11. free(cur);
    12. cur= NULL;
    13. }

    8.链表的销毁

    思路
    结点逐一free,最后记得把*pphead改为最后的cur。

    1. void SLTDel(SLTNode** pphead)
    2. {
    3. assert(*pphead);
    4. SLTNode* cur = *pphead;
    5. SLTNode* prev = cur;
    6. while (cur)
    7. {
    8. prev = cur->next;
    9. free(cur);
    10. cur = prev;
    11. }
    12. *pphead = cur;
    13. }

    关于其他的一些细节问题

    为什么不像顺序表一样写初始化函数?

    可写可不写,这里结构体里面的数据比较少,我们在测试代码的时候直接用指针指向了一块空间。

    为什么要传二级指针?

    想要改变变量的数值而不会因为栈帧的销毁而销毁,就得取到该变量的地址;

    什么时候要传二级指针,什么时候要传一级指针?

    想要改变变量里的数值就要传址调用,二级指针用来接收一级指针;

    如果只是简单的访问就用传值调用

    整个程序

    .h文件

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. #include
    3. #include
    4. #include
    5. typedef int SLTDataType;
    6. typedef struct SLTlist
    7. {
    8. SLTDataType Data;
    9. struct SLTlist * next;
    10. }SLTNode;
    11. void SLTPrint(SLTNode* phead);
    12. void SLTPushBack(SLTNode** pphead, SLTDataType x);
    13. void SLTPopBack(SLTNode** pphead);
    14. void SLTPushFront(SLTNode** pphead, SLTDataType x);
    15. void SLTPopFront(SLTNode** pphead);
    16. SLTNode* SListFind(SLTNode* phead, SLTDataType x);
    17. void SLTInsertAfter( SLTNode* pos, SLTDataType x);
    18. void SListEraseAfter( SLTNode* pos);
    19. void SLTDel(SLTNode** pphead);

    .c文件

    1. #include"SLlist.h"
    2. void SLTPrint(SLTNode* phead)
    3. {
    4. SLTNode* cur = phead;
    5. while (cur)
    6. {
    7. printf("%d->", cur->Data);
    8. cur = cur->next;
    9. }
    10. printf("NULL\n");
    11. }
    12. SLTNode* SLTNewnode(SLTDataType x)
    13. {
    14. SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    15. if (newnode == NULL)
    16. {
    17. perror("melloc fail");
    18. return NULL;
    19. }
    20. newnode->Data = x;
    21. newnode->next = NULL;
    22. return newnode;
    23. }
    24. void SLTPushBack(SLTNode** pphead, SLTDataType x)
    25. {
    26. //创建节点
    27. SLTNode* newnode = SLTNewnode(x);
    28. if (newnode == NULL)
    29. {
    30. return;
    31. }
    32. //情况一:pphead为空
    33. if (*pphead == NULL)
    34. {
    35. *pphead = newnode;
    36. }
    37. //情况二:pphead不为空
    38. else
    39. {
    40. //找到尾节点
    41. SLTNode* tail = *pphead;
    42. while (tail->next)
    43. {
    44. tail = tail->next;
    45. }
    46. tail->next = newnode;
    47. }
    48. }
    49. void SLTPopBack(SLTNode** pphead)
    50. {
    51. assert(*pphead);
    52. //只有一个节点
    53. if ((*pphead)->next == NULL)
    54. {
    55. free(*pphead);
    56. *pphead = NULL;
    57. }
    58. else
    59. {
    60. //找尾置空
    61. SLTNode* tail = *pphead;
    62. while (tail->next->next)
    63. {
    64. tail = tail->next;
    65. }
    66. free(tail->next);
    67. tail->next = NULL;
    68. }
    69. }
    70. void SLTPushFront(SLTNode** pphead, SLTDataType x)
    71. {
    72. SLTNode* newnode = SLTNewnode(x);
    73. if (newnode == NULL)
    74. {
    75. return;
    76. }
    77. newnode->next = *pphead;
    78. *pphead = newnode;
    79. }
    80. void SLTPopFront(SLTNode** pphead)
    81. {
    82. assert(*pphead);
    83. SLTNode* head = *pphead;
    84. *pphead = head->next;
    85. free(head);
    86. head = NULL;
    87. }
    88. SLTNode* SListFind(SLTNode* phead, SLTDataType x)
    89. {
    90. SLTNode* cur = phead;
    91. while (cur)
    92. {
    93. if (cur->Data == x)
    94. {
    95. return cur;
    96. }
    97. cur = cur->next;
    98. }
    99. //如果没找到就返回NULL
    100. return NULL;
    101. }
    102. void SLTInsertAfter( SLTNode* pos, SLTDataType x)
    103. {
    104. assert(pos);
    105. SLTNode* newnode = SLTNewnode(x);
    106. if (newnode == NULL)
    107. {
    108. return;
    109. }
    110. newnode->next = pos->next;
    111. pos->next = newnode;
    112. }
    113. void SListEraseAfter( SLTNode* pos)
    114. {
    115. assert(pos);
    116. if (pos->next == NULL)
    117. {
    118. printf("已是最后一个,不能删\n");
    119. return;
    120. }
    121. SLTNode* cur = pos->next;
    122. pos->next = pos->next->next;
    123. free(cur);
    124. cur= NULL;
    125. }
    126. void SLTDel(SLTNode** pphead)
    127. {
    128. assert(*pphead);
    129. SLTNode* cur = *pphead;
    130. SLTNode* prev = cur;
    131. while (cur)
    132. {
    133. prev = cur->next;
    134. free(cur);
    135. cur = prev;
    136. }
    137. *pphead = cur;
    138. }

  • 相关阅读:
    095:vue+openlayers 地图上添加网格线 (示例代码)
    Web后端的前端:揭秘跨界融合的深度探索
    AspectJ切面自定义注解实现参数分组校验——基础概念(2)
    springboot实现全局事务管理
    DFT简介(TODO)
    企业微信公众号怎么运营管理?
    第三章《数组与循环》第7节:break与continue关键字
    AR远程协同能给企业带来哪些好处?广州华锐互动带你了解!
    java-net-php-python-net本科生毕业设计选导师系统演示录像2019计算机毕业设计程序
    线程是如何实现的?
  • 原文地址:https://blog.csdn.net/lsj1345714539566/article/details/134485364