当前位置:   article > 正文

【数据结构】多图警告!单链表的操作实现以及原理_单链表的基本操作实验原理

单链表的基本操作实验原理

仙人有待乘黄鹤,海客无心随白鸥。                                                                      ——李白

目录

一、单链表的基本知识

1.1单链表

​编辑 

1.2单链表的基本操作

二、单链表的实现

2.1单链表的打印

2.2单链表的头插尾插

2.3单链表的头删尾删

2.4单链表的查找

2.5单链表的特定位置增删

2.6完整实现代码


一、单链表的基本知识

1.1单链表

单链表是一种线性的数据结构,相较于顺序表,单链表进行数据操作的效率会优化很多。单链表使用任意一组存储单元来保存线性的数据,并不需要使用连续地址的存储位置,仅通过指针进行访问下一元素,。因此,单链表的物理结构并不要求像逻辑结构一样连续。

单链表的定义可以使用结构体完成

  1. typedef int SLTDataType;//数据类型
  2. typedef struct SListNode
  3. {
  4. SLTDataType data;
  5. struct SListNode* next;//指向结构体的下一个位置,使数据链式连接
  6. }SLTNode;

1.2单链表的基本操作

单链表作为一种链式数据存储结构,能实现的操作有查看数据(打印)、增加数据(头插尾插)、删除数据(头删尾删)、插入数据、查找特定数据、删除特定位置的数据等操作。

相较于由数组模拟的顺序表而言,结构体形成的单链表无需在意容量大小,并且在插入数据的时候也不用一次次遍历整个链表。


二、单链表的实现

2.1单链表的打印

由单链表的逻辑结构可知,单链表是通过next指针指向下一个元素的,那么一个指针从头开始遍历整个链表一直到NULL,即可完成单链表的打印。话不多说,上代码

  1. void SLTPrint(SLTNode* phead)
  2. {
  3. SLTNode* cur = phead;//头指针
  4. //遍历链表打印
  5. while (cur)
  6. {
  7. printf("%d->", cur->data);
  8. cur = cur->next;
  9. }
  10. printf("NULL\n");
  11. }

2.2单链表的头插尾插

如何在原有链式结构上添加新数据?答案是看物理结构。那么到底怎么实现呢?答案还是看物理结构。

既然链表是由结构体实现的,那么对于一个要插入的数据data,我们首先要为它开辟一块结构体空间。

此时再来观察链表的物理结构,很显然我们只需改变箭头的方向(即next指向的位置)便可完成数据的插入。 

 上代码

  1. SLTNode* BuySLTNode(SLTDataType x)
  2. {
  3. //为新数据开辟空间
  4. SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  5. if (newnode == NULL)
  6. {
  7. perror("malloc fail");
  8. return NULL;//开辟失败
  9. }
  10. newnode->data = x;
  11. newnode -> next = NULL;
  12. return newnode;
  13. }
  14. void SLTPushBack(SLTNode** pphead, SLTDataType x)
  15. {
  16. assert(pphead);
  17. SLTNode* newnode = BuySLTNode(x);
  18. //case1:空链表
  19. if (*pphead == NULL)
  20. {
  21. *pphead = newnode;
  22. }
  23. //case2:非空链表
  24. else
  25. {
  26. //找尾
  27. SLTNode* tail = *pphead;
  28. while (tail->next != NULL)
  29. {
  30. tail = tail->next;
  31. }
  32. tail->next = newnode;
  33. }
  34. }
  35. void SLTPushFront(SLTNode** pphead, SLTDataType x)
  36. {
  37. assert(pphead);
  38. SLTNode* newnode = BuySLTNode(x);
  39. newnode -> next = *pphead;//头插
  40. *pphead= newnode;//换头
  41. }

2.3单链表的头删尾删

有了上面头插尾插的经验,相信大家对头删尾删也有了初步的想法。没错,将next跳过下一个位置即可完成链表的删除,但是仍有一些细节需要注意。

特别注意的是,由于尾删时需要先找到尾节点的位置,如果我们直接将尾节点置为空,则会形成尾指针为野指针的现象,因此我们要找尾节点的上一个next并将其置为空。

看代码

  1. void SLTPopFront(SLTNode** pphead)
  2. {
  3. assert(*pphead);
  4. assert(pphead);
  5. SLTNode* first = *pphead;
  6. *pphead = first->next;
  7. free(first);
  8. first = NULL;
  9. }
  10. void SLTPopBack(SLTNode** pphead)
  11. {
  12. assert(*pphead);
  13. assert(pphead);
  14. //case1:一个节点
  15. if ((*pphead)->next == NULL)
  16. {
  17. free(*pphead);
  18. *pphead = NULL;
  19. }
  20. //case2:多个节点
  21. else
  22. {
  23. //找尾
  24. SLTNode* tail = *pphead;
  25. //尾节点的上一个节点的next置空 不然尾节点则为野指针
  26. while (tail->next->next != NULL)
  27. {
  28. tail = tail->next;
  29. }
  30. free(tail->next);
  31. tail -> next = NULL;
  32. }
  33. }

2.4单链表的查找

查找即简单遍历链表,与打印相似,在此不过多介绍,直接看代码吧。

  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. return NULL;
  13. }

2.5单链表的特定位置增删

对于给定的pos位置进行增删,原理与头尾的操作相似,但是如果pos在链表中间位置,则需调整pos位置的前一个的next,当然如果pos本身就是头尾,那其实与上面的操作无异。

  1. //pos位置插入
  2. void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
  3. {
  4. assert(pphead);
  5. assert(pos);
  6. if (pos == *pphead)
  7. {
  8. SLTPushFront(pphead, x);
  9. }
  10. else
  11. {
  12. //pos的前一个位置
  13. SLTNode* prev = *pphead;
  14. while (prev->next != pos)
  15. {
  16. prev = prev->next;
  17. }
  18. SLTNode* newnode = BuySLTNode(x);
  19. prev->next = newnode;
  20. newnode->next = pos;
  21. }
  22. }
  23. //pos位置删除
  24. void SListErase(SLTNode** pphead, SLTNode* pos)
  25. {
  26. assert(pphead);
  27. assert(pos);
  28. if (pos == *pphead)
  29. {
  30. SLTPopFront(pphead);
  31. }
  32. else
  33. {
  34. SLTNode* prev = *pphead;
  35. while (prev->next != pos)
  36. {
  37. prev = prev->next;
  38. }
  39. prev->next = pos->next;
  40. free(pos);
  41. pos = NULL;
  42. }
  43. }
  44. //pos位置的后一个插入
  45. void SListInsertAfter(SLTNode* pos, SLTDataType x)
  46. {
  47. assert(pos);
  48. SLTNode* newnode = BuySLTNode(x);
  49. newnode->next = pos->next;
  50. pos->next = newnode;
  51. }
  52. //pos位置的后一个删除
  53. void SListEraseAfter(SLTNode* pos)
  54. {
  55. assert(pos);
  56. assert(pos->next);
  57. SLTNode* del = pos->next;
  58. pos->next = del->next;
  59. free(del);
  60. del = NULL;
  61. }

2.6完整实现代码

  1. //SList.h 头文件
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <assert.h>
  5. typedef int SLTDataType;
  6. typedef struct SListNode
  7. {
  8. SLTDataType data;
  9. struct SListNode* next;
  10. }SLTNode;
  11. void SLTPrint(SLTNode* phead);
  12. void SLTPushBack(SLTNode** pphead, SLTDataType x);
  13. void SLTPushFront(SLTNode** pphead, SLTDataType x);
  14. void SLTPopBack(SLTNode** pphead);
  15. void SLTPopFront(SLTNode** pphead);
  16. SLTNode* SListFind(SLTNode* pead, SLTDataType x);
  17. void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
  18. void SListErase(SLTNode** pphead, SLTNode* pos);
  19. void SListInsertAfter(SLTNode* pos, SLTDataType x);
  20. void SListEraseAfter(SLTNode* pos);
  1. //Slist.c源文件
  2. #include "SList.h"
  3. void SLTPrint(SLTNode* phead)
  4. {
  5. SLTNode* cur = phead;
  6. //遍历链表打印
  7. while (cur)
  8. {
  9. printf("%d->", cur->data);
  10. cur = cur->next;
  11. }
  12. printf("NULL\n");
  13. }
  14. SLTNode* BuySLTNode(SLTDataType x)
  15. {
  16. //开辟空间
  17. SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  18. if (newnode == NULL)
  19. {
  20. perror("malloc fail");
  21. return NULL;
  22. }
  23. newnode->data = x;
  24. newnode -> next = NULL;
  25. return newnode;
  26. }
  27. void SLTPushBack(SLTNode** pphead, SLTDataType x)
  28. {
  29. assert(pphead);
  30. SLTNode* newnode = BuySLTNode(x);
  31. //case1:空链表
  32. if (*pphead == NULL)
  33. {
  34. *pphead = newnode;
  35. }
  36. //case2:非空链表
  37. else
  38. {
  39. //找尾
  40. SLTNode* tail = *pphead;
  41. while (tail->next != NULL)
  42. {
  43. tail = tail->next;
  44. }
  45. tail->next = newnode;
  46. }
  47. }
  48. void SLTPushFront(SLTNode** pphead, SLTDataType x)
  49. {
  50. assert(pphead);
  51. SLTNode* newnode = BuySLTNode(x);
  52. newnode -> next = *pphead;//头插
  53. *pphead= newnode;//换头
  54. }
  55. void SLTPopBack(SLTNode** pphead)
  56. {
  57. assert(*pphead);
  58. assert(pphead);
  59. //case1:一个节点
  60. if ((*pphead)->next == NULL)
  61. {
  62. free(*pphead);
  63. *pphead = NULL;
  64. }
  65. //case2:多个节点
  66. else
  67. {
  68. //找尾
  69. SLTNode* tail = *pphead;
  70. //尾节点的上一个节点的next置空 不然尾节点则为野指针
  71. while (tail->next->next != NULL)
  72. {
  73. tail = tail->next;
  74. }
  75. free(tail->next);
  76. tail -> next = NULL;
  77. }
  78. }
  79. void SLTPopFront(SLTNode** pphead)
  80. {
  81. assert(*pphead);
  82. assert(pphead);
  83. SLTNode* first = *pphead;
  84. *pphead = first->next;
  85. free(first);
  86. first = 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. return NULL;
  100. }
  101. //pos位置插入
  102. void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
  103. {
  104. assert(pphead);
  105. assert(pos);
  106. if (pos == *pphead)
  107. {
  108. SLTPushFront(pphead, x);
  109. }
  110. else
  111. {
  112. //pos的前一个位置
  113. SLTNode* prev = *pphead;
  114. while (prev->next != pos)
  115. {
  116. prev = prev->next;
  117. }
  118. SLTNode* newnode = BuySLTNode(x);
  119. prev->next = newnode;
  120. newnode->next = pos;
  121. }
  122. }
  123. //pos位置删除
  124. void SListErase(SLTNode** pphead, SLTNode* pos)
  125. {
  126. assert(pphead);
  127. assert(pos);
  128. if (pos == *pphead)
  129. {
  130. SLTPopFront(pphead);
  131. }
  132. else
  133. {
  134. SLTNode* prev = *pphead;
  135. while (prev->next != pos)
  136. {
  137. prev = prev->next;
  138. }
  139. prev->next = pos->next;
  140. free(pos);
  141. pos = NULL;
  142. }
  143. }
  144. //pos位置的后一个插入
  145. void SListInsertAfter(SLTNode* pos, SLTDataType x)
  146. {
  147. assert(pos);
  148. SLTNode* newnode = BuySLTNode(x);
  149. newnode->next = pos->next;
  150. pos->next = newnode;
  151. }
  152. //pos位置的后一个删除
  153. void SListEraseAfter(SLTNode* pos)
  154. {
  155. assert(pos);
  156. assert(pos->next);
  157. SLTNode* del = pos->next;
  158. pos->next = del->next;
  159. free(del);
  160. del = NULL;
  161. }

本章到此就结束了,希望大家可以对单链表有一个清晰的认识。

(最近压力有些大,这几天有隐隐摆烂的痕迹,要从现在找回状态,加油!)

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

闽ICP备14008679号