当前位置:   article > 正文

[数据结构]单链表(C语言版)_单链表c语言

单链表c语言

在学习单链表之前我们已经学习了顺序表相关的基本操作,顺序表访问元素更加方便,物理地址是连续的;但是也有一些缺点:

1.在头部插入或者从中间插入或删除元素时需要搬移数据,效率较低

2.在插入数据时可能存在空间不足的情况,需要扩容

因此就会出现另一种线性表---链表

1.链表的概念

链表顾名思义就是链式的存储结构,元素的逻辑顺序是由指针来依次连接的。

链表中有多个节点,每一个节点里储存着数据,还有指向下一个节点的指针 。

2.单链表的定义

我们要实现的是不带头结点的非循环单向链表

单链表的结构包括数值域和指针域

  1. typedef int DataType;
  2. typedef struct SListNode
  3. {
  4. DataType data;
  5. struct SListNode* next;//指向下一个节点
  6. }SListNode;

3.单链表节点的创建

  1. SListNode* BuySListNode(DataType x)
  2. {
  3. SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));//这个动态内存空间是用来存放这个结构体类型的
  4. if (NULL == newNode)
  5. {
  6. printf("创建节点失败!!!!");
  7. exit(0);
  8. }
  9. newNode->data = x;
  10. newNode->next = NULL;
  11. return newNode;
  12. }

4.单链表的尾插

  1. void SListPushBack(SListNode** pplist,DataType x)
  2. {
  3. assert(*pplist);
  4. //如果pplist为空则需要让pplist指向刚刚插入的节点
  5. if (NULL == *pplist)
  6. {
  7. *pplist = BuySListNode(x);
  8. }
  9. else
  10. {
  11. //此时链表不为空
  12. //1.构建一个新节点
  13. SListNode* NewNode = BuySListNode(x);
  14. //2.找出原链表中的最后一个节点
  15. SListNode* cur = *pplist;
  16. while (cur->next)
  17. {
  18. //获取cur的下一个节点
  19. cur = cur->next;
  20. }
  21. //3.插入新节点
  22. cur->next = NewNode;
  23. }
  24. }

5.单链表的尾删

  1. //单链表的尾删
  2. void SListPopBack(SListNode** pplist)
  3. {
  4. assert(pplist);//保证pplist指向实参
  5. //1.空链表
  6. if (NULL == *pplist)
  7. {
  8. return;
  9. }
  10. else if (NULL == (*pplist)->next)
  11. {
  12. //2.链表中只有一个节点
  13. free(*pplist);
  14. *pplist = NULL;
  15. }
  16. else
  17. {
  18. //3.链表中有多个节点
  19. //a.找倒数第一个节点
  20. SListNode* cur = *pplist;
  21. SListNode* prev = NULL;//保存cur前一个节点
  22. while (cur->next)
  23. {
  24. prev = cur;
  25. cur = cur->next;
  26. }
  27. prev->next = NULL;
  28. free(cur);
  29. //b.找倒数第二个节点
  30. /*
  31. SListNode* cur = *pplist;
  32. while(cur->next->next)
  33. {
  34. cur = cur->next;
  35. }
  36. free(cur->next);
  37. cur->next = NULL;
  38. */
  39. //c.错误写法
  40. /*while (cur->next)
  41. {
  42. cur = cur->next;
  43. }
  44. free(cur);*/
  45. }
  46. }

6.单链表打印

  1. //单链表打印
  2. void SListPrint(SListNode* plist)
  3. {
  4. SListNode* cur = plist;
  5. while (cur)
  6. {
  7. printf("&d--->", cur->data);
  8. cur = cur->next;
  9. }
  10. printf("NULL\n");//单链表最后一个节点指向空
  11. }

7.单链表头插

  1. // 单链表的头插
  2. //时间复杂度:O(1)
  3. void SListPushFront(SListNode** pplist, SLTDataType x)
  4. {
  5. //assert(pplist);
  6. //SListNode* newNode = BuySListNode(x);
  7. 1.链表为空
  8. //if (NULL == *pplist)
  9. //{
  10. // *pplist = newNode;
  11. //}
  12. //else
  13. //{
  14. // //2.链表不空
  15. // newNode->next = *pplist;
  16. // *pplist = newNode;
  17. //}
  18. assert(pplist);
  19. //表面看上需要分情况讨论,但是在分析之后情况一可以与情况二合并
  20. SListNode* newNode = BuySListNode(x);
  21. newNode->next = *pplist;
  22. *pplist = newNode;
  23. }

8.单链表头删

  1. //单链表头删
  2. void SListPopFront(SListNode** pplist)
  3. {
  4. assert(pplist);
  5. //1.链表为空
  6. if (NULL == *pplist)
  7. {
  8. return;
  9. }
  10. //else if (NULL == (*pplist)->next)
  11. //{
  12. // //2.链表只有一个节点
  13. // free(*pplist);
  14. // *pplist = NULL;
  15. //}
  16. else
  17. {
  18. //3.链表中有多个节点(分析后可知也包含一个节点的情况)
  19. SListNode* del = *pplist;//将要删除的节点保存起来
  20. *pplist = (*pplist)->next;
  21. free(del);
  22. }
  23. }

9.单链表删除任意位置pos之后的值

  1. // 单链表删除pos位置之后的值
  2. //时间复杂度O(1)
  3. void SListEraseAfter(SListNode* pos)
  4. {
  5. SListNode* delNode = NULL;
  6. if (NULL == pos)
  7. {
  8. return;
  9. }
  10. delNode = pos->next;
  11. if (NULL == delNode)
  12. {
  13. return;
  14. }
  15. //pos->data = delNode->data;
  16. pos->next = delNode->next;
  17. free(delNode);
  18. }

代码中加一句pos->data = delNode->data就可以实现删除任意位置的值

但是不能删除最后一个节点位置的元素

10.单链表在任意位置pos之后插入

  1. //单链表在任意位置pos之后插入
  2. //时间复杂度:O(1)
  3. void SListInsertAfter(SListNode* pos, SLTDataType x)
  4. {
  5. SListNode* newNode = NULL;
  6. if (NULL == pos)
  7. {
  8. return;
  9. }
  10. newNode = BuySListNode(x);
  11. newNode->next = pos->next;//必须先让新节点接入到链表内然后将pos位置的节点指向新节点
  12. pos->next = newNode;
  13. }

新节点无法插入到pos之前,因为该方法未提供plist头节点

11.单链表在任意位置pos之前插入

  1. //单链表在任意位置pos之前插入
  2. void SListInsertBefore(SListNode** pplist, SListNode* pos, SLTDataType x)
  3. {
  4. assert(pplist);
  5. assert(pos);
  6. SListNode* newNode = NULL;
  7. if (pos == *pplist)
  8. {
  9. newNode = BuySListNode(x);
  10. }
  11. else
  12. {
  13. SListNode* cur = *pplist;//将头节点保存起来,**pplist是plist的地址,*plist才是解引用后的指针
  14. while (cur->next != pos)
  15. {
  16. cur = cur->next;
  17. }
  18. newNode = BuySListNode(x);
  19. newNode->next = pos;
  20. cur->next = newNode;
  21. }
  22. }

11.单链表的销毁

  1. //单链表销毁
  2. void SListDestroy(SListNode** pplist)
  3. {
  4. assert(pplist);
  5. SListNode* cur = *pplist;
  6. while (cur)
  7. {
  8. *pplist = cur->next;
  9. free(cur);
  10. cur = *pplist;
  11. }
  12. *pplist = NULL;
  13. }

单链表的销毁很简单,只需要用cur把链表遍历一遍,每次让pplist头指针指向cur->next,释放cur所指向的当前节点,然后更新pplist指针的值,最后将pplist指向NULL。

12.单链表操作的测试

  1. void TestSList1()
  2. {
  3. SListNode* plist = NULL;//单链表的起始位置保存
  4. //插入第一个节点时需要在此函数中通过形参来达到修改plist指向的修改
  5. SListPushBack(&plist, 1);//依次尾插1,2,3,4,5
  6. SListPrint(plist);
  7. SListPushBack(&plist, 2);
  8. SListPushBack(&plist, 3);
  9. SListPushBack(&plist, 4);
  10. SListPushBack(&plist, 5);
  11. SListPrint(plist);
  12. SListPopBack(&plist);//尾删
  13. SListPrint(plist);
  14. SListPopBack(&plist);
  15. SListPrint(plist);
  16. SListPopBack(&plist);
  17. SListPrint(plist);
  18. SListPopBack(&plist);
  19. SListPrint(plist);
  20. SListPopBack(&plist);
  21. SListPrint(plist);
  22. SListPushFront(&plist, 1);//依次头插1,2,3,4,5
  23. SListPrint(plist);
  24. SListPushFront(&plist, 2);
  25. SListPrint(plist);
  26. SListPushFront(&plist, 3);
  27. SListPrint(plist);
  28. SListPushFront(&plist, 4);
  29. SListPrint(plist);
  30. SListPushFront(&plist, 5);
  31. SListPrint(plist);
  32. SListInsertAfter(SListFind(plist, 3), 7);//在3后面插入7
  33. SListPrint(plist);
  34. printf("节点数为 %d\n", SListSize(plist));
  35. SListInsertAfter(SListFind(plist, 1), 8);//在1后面插入8
  36. SListPrint(plist);
  37. printf("节点数为 %d\n", SListSize(plist));
  38. SListInsertAfter(SListFind(plist, 2), 9);//在2后面插入9
  39. SListPrint(plist);
  40. printf("节点数为 %d\n", SListSize(plist));
  41. SListEraseAfter(SListFind(plist, 3));//删除3后面的元素
  42. SListPrint(plist);
  43. printf("节点数为 %d\n", SListSize(plist));
  44. SListEraseAfter(SListFind(plist, 1));//删除1后面的元素
  45. SListPrint(plist);
  46. printf("节点数为 %d\n", SListSize(plist));
  47. SListEraseAfter(SListFind(plist, 2));//删除2后面的元素
  48. SListPrint(plist);
  49. printf("节点数为 %d\n", SListSize(plist));
  50. SListInsertBefore(&plist, SListFind(plist, 1), 6);//在1之前插入元素6
  51. SListPrint(plist);
  52. printf("节点数为 %d\n",SListSize(plist));
  53. }
  54. void TestSList2()
  55. {
  56. SListNode* plist = NULL;//单链表的起始位置保存
  57. //插入第一个节点时需要在此函数中通过形参来达到修改plist指向的修改
  58. SListPushBack(&plist, 1);//依次尾插1,2,3,4,5
  59. SListPrint(plist);
  60. SListPushBack(&plist, 2);
  61. SListPushBack(&plist, 3);
  62. SListPushBack(&plist, 4);
  63. SListPushBack(&plist, 5);
  64. SListPrint(plist);
  65. SListPopBack(&plist);//尾删
  66. SListPrint(plist);
  67. SListPopBack(&plist);
  68. SListPrint(plist);
  69. SListPopBack(&plist);
  70. SListPrint(plist);
  71. SListPopBack(&plist);
  72. SListPrint(plist);
  73. SListPopBack(&plist);
  74. SListPrint(plist);
  75. SListPushFront(&plist, 1);//依次头插1,2,3,4,5
  76. SListPrint(plist);
  77. SListPushFront(&plist, 2);
  78. SListPrint(plist);
  79. SListPushFront(&plist, 3);
  80. SListPrint(plist);
  81. SListPushFront(&plist, 4);
  82. SListPrint(plist);
  83. SListPushFront(&plist, 5);
  84. SListPrint(plist);
  85. SListPopFront(&plist);//头删
  86. SListPrint(plist);
  87. printf("节点数为 %d\n", SListSize(plist));
  88. SListPopFront(&plist);
  89. SListPrint(plist);
  90. printf("节点数为 %d\n", SListSize(plist));
  91. SListPopFront(&plist);
  92. SListPrint(plist);
  93. printf("节点数为 %d\n", SListSize(plist));
  94. SListPopFront(&plist);
  95. SListPrint(plist);
  96. printf("节点数为 %d\n", SListSize(plist));
  97. SListPopFront(&plist);
  98. SListPrint(plist);
  99. printf("节点数为 %d\n", SListSize(plist));
  100. }

 

本文全部代码:

SList.h

  1. #pragma once
  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. }SListNode;
  11. //动态申请一个节点
  12. SListNode* BuySListNode(SLTDataType x);
  13. //单链表的尾插
  14. void SListPushBack(SListNode** pplist, SLTDataType x);
  15. //单链表的尾删
  16. void SListPopBack(SListNode** pplist);
  17. //链表打印
  18. void SListPrint(SListNode* plist);
  19. // 单链表的头插
  20. void SListPushFront(SListNode** pplist, SLTDataType x);
  21. // 单链表头删
  22. void SListPopFront(SListNode** pplist);
  23. // 单链表查找
  24. SListNode* SListFind(SListNode* plist, SLTDataType x);
  25. // 单链表在pos位置之后插入x
  26. // 分析思考为什么不在pos位置之前插入?
  27. void SListInsertAfter(SListNode* pos, SLTDataType x);
  28. // 单链表删除pos位置之后的值
  29. // 分析思考为什么不删除pos位置?
  30. void SListEraseAfter(SListNode* pos);
  31. // 单链表的销毁
  32. void SListDestroy(SListNode** plist);

SList.c 

  1. #include "SList.h"
  2. //动态申请一个节点
  3. SListNode* BuySListNode(SLTDataType x)
  4. {
  5. SListNode* newNode = (SListNode *) malloc(sizeof(SListNode));//这个动态内存空间是用来存放这个结构体类型的
  6. if (NULL == newNode)
  7. {
  8. printf("创建节点失败!!!!");
  9. exit(0);
  10. }
  11. newNode->data = x;
  12. newNode->next = NULL;
  13. return newNode;
  14. }
  15. //单链表的尾插
  16. void SListPushBack(SListNode** pplist, SLTDataType x)
  17. {
  18. //若为空链表则需要让pplist指向刚刚插入的节点
  19. if (NULL == *pplist)
  20. {
  21. *pplist = BuySListNode(x);
  22. }
  23. else
  24. {
  25. //此时链表不为空
  26. //1.构建一个新节点
  27. SListNode* newNode = BuySListNode(x);
  28. //2.找原链表中最后一个节点
  29. SListNode* cur = *pplist;
  30. while (cur->next)
  31. {
  32. //获取cur的下一个节点
  33. cur = cur->next;
  34. }
  35. //3.插入新节点
  36. cur->next = newNode;
  37. }
  38. }
  39. //单链表的尾删
  40. void SListPopBack(SListNode** pplist)
  41. {
  42. assert(pplist);
  43. //1.空链表
  44. if (NULL == *pplist)
  45. {
  46. return;
  47. }
  48. else if(NULL == (*pplist)->next)
  49. {
  50. //2.链表中只有一个节点
  51. free(*pplist);
  52. *pplist = NULL;
  53. }
  54. else
  55. {
  56. //3.链表中有多个节点
  57. SListNode* cur = *pplist;//*pplist为第一个节点
  58. while (cur->next->next)
  59. {
  60. cur = cur->next;
  61. }
  62. free(cur->next);
  63. cur->next = NULL;
  64. //方法2
  65. /*
  66. SListNode* cur = *pplist;//*pplist为第一个节点
  67. SListNode* prev = NULL;
  68. while (cur->next)
  69. {
  70. prve = cur;
  71. cur = cur->next;
  72. }
  73. free(cur);
  74. prve->next = NULL;
  75. */
  76. }
  77. }
  78. //链表打印
  79. void SListPrint(SListNode* plist)
  80. {
  81. SListNode* cur = plist;
  82. while (cur)
  83. {
  84. printf("%d--->", cur->data);
  85. cur = cur->next;
  86. }
  87. printf("NULL\n");
  88. }
  89. // 单链表的头插
  90. //时间复杂度:O(1)
  91. void SListPushFront(SListNode** pplist, SLTDataType x)
  92. {
  93. assert(pplist);
  94. SListNode* newNode = BuySListNode(x);
  95. 1.链表为空
  96. //if (NULL == *pplist)
  97. //{
  98. // *pplist = newNode;
  99. //}
  100. //else
  101. //{
  102. // //2.链表不空
  103. // newNode->next = *pplist;
  104. // *pplist = newNode;
  105. //}
  106. //表面看上需要分情况讨论,但是在分析之后情况一可以与情况二合并
  107. newNode->next = *pplist;
  108. *pplist = newNode;
  109. }
  110. //单链表查找
  111. SListNode* SListFind(SListNode* plist, SLTDataType x)
  112. {
  113. SListNode* cur = plist;
  114. while (cur)
  115. {
  116. if (cur->data == x)
  117. {
  118. return cur;
  119. }
  120. cur = cur->next;
  121. }
  122. return NULL;
  123. }
  124. //单链表中的有效节点数
  125. int SListSize(SListNode* plist)
  126. {
  127. int size = 0;
  128. SListNode* cur = plist;
  129. while (cur)
  130. {
  131. size++;
  132. cur = cur->next;
  133. }
  134. return size;
  135. }
  136. //单链表头删
  137. void SListPopFront(SListNode** pplist)
  138. {
  139. assert(pplist);
  140. SListNode* del = NULL;
  141. //1.链表为空
  142. if (NULL == *pplist)
  143. {
  144. return;
  145. }
  146. //else if (NULL == (*pplist)->next)
  147. //{
  148. // //2.链表只有一个节点
  149. // free(*pplist);
  150. // *pplist = NULL;
  151. //}
  152. else
  153. {
  154. //3.链表中有多个节点(分析后可知也包含一个节点的情况)
  155. SListNode* del = *pplist;//将要删除的节点保存起来
  156. *pplist = (*pplist)->next;
  157. free(del);
  158. }
  159. }
  160. //单链表在任意位置pos之后插入
  161. //新节点无法插入到pos之前,因为该方法未提供plist头节点
  162. //时间复杂度:O(1)
  163. void SListInsertAfter(SListNode* pos, SLTDataType x)
  164. {
  165. SListNode* newNode = NULL;
  166. if (NULL == pos)
  167. {
  168. return;
  169. }
  170. newNode = BuySListNode(x);
  171. newNode->next = pos->next;//必须先让新节点接入到链表内然后将pos位置的节点指向新节点
  172. pos->next = newNode;
  173. }
  174. //单链表在任意位置pos之前插入
  175. void SListInsertBefore(SListNode** pplist, SListNode* pos, SLTDataType x)
  176. {
  177. assert(pplist);
  178. assert(pos);
  179. SListNode* newNode = NULL;
  180. if (pos == *pplist)
  181. {
  182. newNode = BuySListNode(x);
  183. }
  184. else
  185. {
  186. SListNode* cur = *pplist;//将头节点保存起来,**pplist是plist的地址,*plist才是解引用后的指针
  187. while (cur->next != pos)
  188. {
  189. cur = cur->next;
  190. }
  191. newNode = BuySListNode(x);
  192. newNode->next = pos;
  193. cur->next = newNode;
  194. }
  195. }
  196. // 单链表删除pos位置之后的值
  197. //时间复杂度O(1)
  198. void SListEraseAfter(SListNode* pos)
  199. {
  200. SListNode* delNode = NULL;
  201. if (NULL == pos)
  202. {
  203. return;
  204. }
  205. delNode = pos->next;
  206. if (NULL == delNode)
  207. {
  208. return;
  209. }
  210. //pos->data = delNode->data;
  211. pos->next = delNode->next;
  212. free(delNode);
  213. }
  214. //单链表销毁
  215. void SListDestroy(SListNode** pplist)
  216. {
  217. assert(pplist);
  218. SListNode* cur = *pplist;
  219. while (cur)
  220. {
  221. *pplist = cur->next;
  222. free(cur);
  223. cur = *pplist;
  224. }
  225. *pplist = NULL;
  226. }
  227. void TestSList1()
  228. {
  229. SListNode* plist = NULL;//单链表的起始位置保存
  230. //插入第一个节点时需要在此函数中通过形参来达到修改plist指向的修改
  231. SListPushBack(&plist, 1);//依次尾插1,2,3,4,5
  232. SListPrint(plist);
  233. SListPushBack(&plist, 2);
  234. SListPushBack(&plist, 3);
  235. SListPushBack(&plist, 4);
  236. SListPushBack(&plist, 5);
  237. SListPrint(plist);
  238. SListPopBack(&plist);//尾删
  239. SListPrint(plist);
  240. SListPopBack(&plist);
  241. SListPrint(plist);
  242. SListPopBack(&plist);
  243. SListPrint(plist);
  244. SListPopBack(&plist);
  245. SListPrint(plist);
  246. SListPopBack(&plist);
  247. SListPrint(plist);
  248. SListPushFront(&plist, 1);//依次头插1,2,3,4,5
  249. SListPrint(plist);
  250. SListPushFront(&plist, 2);
  251. SListPrint(plist);
  252. SListPushFront(&plist, 3);
  253. SListPrint(plist);
  254. SListPushFront(&plist, 4);
  255. SListPrint(plist);
  256. SListPushFront(&plist, 5);
  257. SListPrint(plist);
  258. SListInsertAfter(SListFind(plist, 3), 7);//在3后面插入7
  259. SListPrint(plist);
  260. printf("节点数为 %d\n", SListSize(plist));
  261. SListInsertAfter(SListFind(plist, 1), 8);//在1后面插入8
  262. SListPrint(plist);
  263. printf("节点数为 %d\n", SListSize(plist));
  264. SListInsertAfter(SListFind(plist, 2), 9);//在2后面插入9
  265. SListPrint(plist);
  266. printf("节点数为 %d\n", SListSize(plist));
  267. SListEraseAfter(SListFind(plist, 3));//删除3后面的元素
  268. SListPrint(plist);
  269. printf("节点数为 %d\n", SListSize(plist));
  270. SListEraseAfter(SListFind(plist, 1));//删除1后面的元素
  271. SListPrint(plist);
  272. printf("节点数为 %d\n", SListSize(plist));
  273. SListEraseAfter(SListFind(plist, 2));//删除2后面的元素
  274. SListPrint(plist);
  275. printf("节点数为 %d\n", SListSize(plist));
  276. SListInsertBefore(&plist, SListFind(plist, 1), 6);//在1之前插入元素6
  277. SListPrint(plist);
  278. printf("节点数为 %d\n",SListSize(plist));
  279. }
  280. void TestSList2()
  281. {
  282. SListNode* plist = NULL;//单链表的起始位置保存
  283. //插入第一个节点时需要在此函数中通过形参来达到修改plist指向的修改
  284. SListPushBack(&plist, 1);//依次尾插1,2,3,4,5
  285. SListPrint(plist);
  286. SListPushBack(&plist, 2);
  287. SListPushBack(&plist, 3);
  288. SListPushBack(&plist, 4);
  289. SListPushBack(&plist, 5);
  290. SListPrint(plist);
  291. SListPopBack(&plist);//尾删
  292. SListPrint(plist);
  293. SListPopBack(&plist);
  294. SListPrint(plist);
  295. SListPopBack(&plist);
  296. SListPrint(plist);
  297. SListPopBack(&plist);
  298. SListPrint(plist);
  299. SListPopBack(&plist);
  300. SListPrint(plist);
  301. SListPushFront(&plist, 1);//依次头插1,2,3,4,5
  302. SListPrint(plist);
  303. SListPushFront(&plist, 2);
  304. SListPrint(plist);
  305. SListPushFront(&plist, 3);
  306. SListPrint(plist);
  307. SListPushFront(&plist, 4);
  308. SListPrint(plist);
  309. SListPushFront(&plist, 5);
  310. SListPrint(plist);
  311. SListPopFront(&plist);//头删
  312. SListPrint(plist);
  313. printf("节点数为 %d\n", SListSize(plist));
  314. SListPopFront(&plist);
  315. SListPrint(plist);
  316. printf("节点数为 %d\n", SListSize(plist));
  317. SListPopFront(&plist);
  318. SListPrint(plist);
  319. printf("节点数为 %d\n", SListSize(plist));
  320. SListPopFront(&plist);
  321. SListPrint(plist);
  322. printf("节点数为 %d\n", SListSize(plist));
  323. SListPopFront(&plist);
  324. SListPrint(plist);
  325. printf("节点数为 %d\n", SListSize(plist));
  326. }
  327. int main()
  328. {
  329. //TestSList1();
  330. TestSList2();
  331. return 0;
  332. }

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

闽ICP备14008679号