当前位置:   article > 正文

c语言单向链表

c语言单向链表

c语言实现简单的单向链表


文章目录

一、单向链表是什么?

单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;链表是使用指针进行构造的列表;又称为结点列表,因为链表是由一个个结点组装起来的;其中每个结点都有指针成员变量指向列表中的下一个结点;(下图来自百度)

 

二、功能代码实现

1.包含头文件

  1. #include<Stdio.h>
  2. #include<string.h>
  3. #include<stdlib.h>
  4. #include<assert.h>

2.结构体(结点)

  1. typedef int SLTDataType;//宏定义成员为整形
  2. typedef struct SListNode
  3. {
  4. SLTDataType data;//资料
  5. struct SListNode* next;//连接下一个节点的链子
  6. }SLTNode;

3.定义

  1. SLTNode* BuyNewNode(SLTDataType X);//创建新节点
  2. void SListPrint(SLTNode* phead);//打印
  3. void SListDestory(SLTNode**phead);//销毁
  4. void SListPushFront(SLTNode** pphead, SLTDataType X);//头插
  5. void SListPushBack(SLTNode** pphead, SLTDataType X);//尾插
  6. void SListPopFront(SLTNode** pphead);//头删
  7. void SListPopBack(SLTNode** pphead);//尾删
  8. SLTNode* SListFind(SLTNode* pphead, SLTDataType X);//查询
  9. void SListInsert(SLTNode** pphead,SLTNode*pos, SLTDataType X);//插入-在pos之前插入
  10. void SListInsert( SLTNode* pos, SLTDataType X);//插入-在pos之后插入

4.各类功能实现

SLTNode* BuyNewNode(SLTDataType X);//创建新节点-结构体指针类型函数-传值
  1. SLTNode* BuyNewNode(SLTDataType X)
  2. {
  3. SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//先开辟一块空间给新节点
  4. if (newnode == NULL)//如果新节点为空-即开辟失败则报错
  5. {
  6. perror("malloc fail");
  7. exit(-1);
  8. }
  9. newnode->data = X;//把X的值赋给新节点的data
  10. newnode->next = NULL;//把链子设为空指针
  11. return newnode;//把节点传回去
  12. }

void SListPrint(SLTNode* phead);//打印

  1. void SListPrint(SLTNode* phead)
  2. {
  3. SListNode* cur = phead;//新建一个结构体指针为表头
  4. while (cur != NULL)//如果指针指向的内容不为空则打印内容
  5. {
  6. printf("%d->", cur->data);
  7. cur = cur->next;
  8. }
  9. printf("NULL\n");//为空则打印空指针
  10. }

void SListDestory(SLTNode**phead);//销毁

  1. void SListDestory(SLTNode** pphead)
  2. {
  3. assert(pphead);//指向节点的指针不能为空
  4. SLTNode* cur = *pphead;//新建一个临时节点为链表头
  5. while (cur)//临时节点不为空时
  6. {
  7. SLTNode* next = cur->next;//链表下一个节点为临时节点的的下一个节点
  8. free(cur);//释放当前节点
  9. cur = next;//当前节点为下一个节点
  10. }
  11. *pphead = NULL;//最后释放完链表后置指针为空
  12. }
void SListPushFront(SLTNode** pphead, SLTDataType X);//头插
  1. void SListPushFront(SLTNode** pphead, SLTDataType X)//头插-链表吧不为空,要改变的是SList-用地址
  2. {
  3. assert(pphead);
  4. SLTNode* newnode = BuyNewNode(X);//新建其含有对应内容的新节点
  5. newnode->next = *pphead;//新节点的下一个节点为表头
  6. *pphead = newnode;//新节点充当表头
  7. }
void SListPopFront(SLTNode** pphead);//头删
  1. void SListPopFront(SLTNode** pphead)//头删
  2. {
  3. assert(pphead);
  4. // if (*pphead == NULL)//温柔检查
  5. // {
  6. // return;
  7. //
  8. //}
  9. assert(*pphead != NULL);//暴力检查
  10. SLTNode* del = *pphead;//建立新节点为表头
  11. *pphead = (*pphead)->next;//表头的下一个节点为表头
  12. free(del);//释放表头
  13. del = NULL;//新节点指针置为空
  14. }
void SListPushBack(SLTNode** pphead, SLTDataType X);//尾插
  1. void SListPushBack(SLTNode** pphead, SLTDataType X)//尾插-链表不为空,要改变的是SList 的成员-用结构体指针即可
  2. {
  3. assert(pphead);
  4. SLTNode* newnode = BuyNewNode(X);//新建
  5. if (*pphead == NULL)//一种情况为表头为空-即链表只有一个空节点
  6. {
  7. *pphead = newnode;
  8. }
  9. else//另一种情况要找尾巴
  10. {
  11. //找尾
  12. SLTNode* tail = *pphead;//建立新节点为表头,依次找到表尾
  13. while(tail->next != NULL)
  14. {
  15. tail = tail->next;
  16. }
  17. tail->next = newnode;//表尾替换为含有对应内容的新节点
  18. }
  19. }

void SListPopBack(SLTNode** pphead);//尾删

A-单指针法

  1. void SListPopBack(SLTNode** pphead)//尾删
  2. {
  3. // if (*pphead == NULL)//温柔检查
  4. // {
  5. // return;
  6. //
  7. //}
  8. assert(*pphead != NULL);//暴力检查
  9. assert(pphead);
  10. //对应链表只有一个节点-直接释放
  11. if ((*pphead)->next == NULL)
  12. {
  13. free(*pphead);
  14. *pphead = NULL;
  15. }
  16. else//对应链表有多个节点
  17. {
  18. //找尾
  19. SLTNode* tail = *pphead;//新建节点为表头-开始找尾
  20. if (tail->next->next != NULL)//表尾为空指针-即要删除表尾前一个节点
  21. {
  22. tail = tail->next;
  23. }
  24. free(tail->next);
  25. tail->next = NULL;
  26. }
  27. }



B-双指针(快慢指针法)

  1. void SListPopBack(SLTNode** pphead)//尾删
  2. {
  3. assert(pphead);
  4. //找尾
  5. SLTNode* tail = *pphead;
  6. SLTNode* open = NULL;
  7. while (tail->next != NULL)
  8. {
  9. open = tail;
  10. tail = tail->next;
  11. }
  12. open->next = NULL;
  13. free(tail->next);
  14. tail = NULL;
  15. }

SLTNode* SListFind(SLTNode* pphead, SLTDataType X);//查询

  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. }

void SListInsert(SLTNode** pphead,SLTNode*pos, SLTDataType X);//插入-在pos之前插入

  1. void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType X)//插入-在pos之前插入
  2. {
  3. assert(pphead);
  4. assert(pos);
  5. if (pos == *pphead)//pos为表头直接用头插
  6. {
  7. SListPushFront(pphead,X);
  8. }
  9. else {
  10. SListNode* pre = *pphead;
  11. while (pre->next != pos)
  12. {
  13. pre = pre->next;
  14. assert(pre);//找到NULL还没找到pos说明pos传错了
  15. }
  16. SListNode* newnode = BuyNewNode(X);//串连起来
  17. pre->next = newnode;
  18. newnode->next = pos;
  19. }
  20. }

void SListInsert( SLTNode* pos, SLTDataType X);//插入-在pos之后插入

  1. void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType X)//插入-在pos之后插入
  2. {
  3. assert(pos);
  4. SListNode* newnode = BuyNewNode(X);
  5. newnode->next = pos->next;
  6. pos->next = newnode;
  7. }

三、代码实现单向链表

SList.h

  1. #include<Stdio.h>
  2. #include<string.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. SLTNode* BuyNewNode(SLTDataType X);//创建新节点
  12. void SListPrint(SLTNode* phead);//打印
  13. void SListDestory(SLTNode**phead);//销毁
  14. void SListPushFront(SLTNode** pphead, SLTDataType X);//头插
  15. void SListPushBack(SLTNode** pphead, SLTDataType X);//尾插
  16. void SListPopFront(SLTNode** pphead);//头删
  17. void SListPopBack(SLTNode** pphead);//尾删
  18. SLTNode* SListFind(SLTNode* pphead, SLTDataType X);//查询
  19. void SListInsert(SLTNode** pphead,SLTNode*pos, SLTDataType X);//插入-在pos之前插入
  20. void SListInsert( SLTNode* pos, SLTDataType X);//插入-在pos之后插入
  21. void SListEarse(SLTNode** pphead,SLTNode*pos);//删除-删除pos

SList.cpp

  1. //顺序表的缺点
  2. //1.中间或中部的插入和删除,时间复杂度为o(n)
  3. //2.增容需要申请空间,拷贝数据,释放旧空间,会有消耗
  4. //3.扩容一般以2倍的增长,易造成空间浪费
  5. //链表
  6. //无头单向非循环单链表:结构简单,一般不会单独用来存数据,实际上更多作为其他数据结构的子结构,如哈希表,图的邻接表
  7. #include"SList.h"
  8. void SListPrint(SLTNode* phead)
  9. {
  10. SListNode* cur = phead;
  11. while (cur != NULL)
  12. {
  13. printf("%d->", cur->data);
  14. cur = cur->next;
  15. }
  16. printf("NULL\n");
  17. }
  18. SLTNode* BuyNewNode(SLTDataType X)
  19. {
  20. SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  21. if (newnode == NULL)
  22. {
  23. perror("malloc fail");
  24. exit(-1);
  25. }
  26. newnode->data = X;
  27. newnode->next = NULL;
  28. return newnode;
  29. }
  30. void SListPushFront(SLTNode** pphead, SLTDataType X)//头插-链表吧不为空,要改变的是SList-用地址
  31. {
  32. assert(pphead);
  33. SLTNode* newnode = BuyNewNode(X);
  34. newnode->next = *pphead;
  35. *pphead = newnode;
  36. }
  37. void SListPushBack(SLTNode** pphead, SLTDataType X)//尾插-链表不为空,要改变的是SList 的成员-用结构体指针即可
  38. {
  39. assert(pphead);
  40. SLTNode* newnode = BuyNewNode(X);
  41. if (*pphead == NULL)
  42. {
  43. *pphead = newnode;
  44. }
  45. else
  46. {
  47. //找尾
  48. SLTNode* tail = *pphead;
  49. while(tail->next != NULL)
  50. {
  51. tail = tail->next;
  52. }
  53. tail->next = newnode;
  54. }
  55. }
  56. void SListPopFront(SLTNode** pphead)//头删
  57. {
  58. assert(pphead);
  59. // if (*pphead == NULL)//温柔检查
  60. // {
  61. // return;
  62. //
  63. //}
  64. assert(*pphead != NULL);//暴力检查
  65. SLTNode* del = *pphead;
  66. *pphead = (*pphead)->next;
  67. free(del);
  68. del = NULL;
  69. }
  70. //void SListPopBack(SLTNode** pphead)//尾删
  71. //{
  72. // assert(pphead);
  73. // //找尾
  74. // SLTNode* tail = *pphead;
  75. // SLTNode* open = NULL;
  76. // while (tail->next != NULL)
  77. // {
  78. // open = tail;
  79. // tail = tail->next;
  80. // }
  81. // open->next = NULL;
  82. // free(tail->next);
  83. // tail = NULL;
  84. //
  85. //}
  86. void SListPopBack(SLTNode** pphead)//尾删
  87. {
  88. // if (*pphead == NULL)//温柔检查
  89. // {
  90. // return;
  91. //
  92. //}
  93. assert(*pphead != NULL);//暴力检查
  94. assert(pphead);
  95. //一个节点
  96. if ((*pphead)->next == NULL)
  97. {
  98. free(*pphead);
  99. *pphead = NULL;
  100. }
  101. else//多个节点
  102. {
  103. //找尾
  104. SLTNode* tail = *pphead;
  105. if (tail->next->next != NULL)
  106. {
  107. tail = tail->next;
  108. }
  109. free(tail->next);
  110. tail->next = NULL;
  111. }
  112. }
  113. void SListDestory(SLTNode** pphead)
  114. {
  115. assert(pphead);
  116. SLTNode* cur = *pphead;
  117. while (cur)
  118. {
  119. SLTNode* next = cur->next;
  120. free(cur);
  121. cur = next;
  122. }
  123. *pphead = NULL;
  124. }
  125. SLTNode* SListFind(SLTNode* phead, SLTDataType X)
  126. {
  127. SLTNode* cur = phead;
  128. while (cur)
  129. {
  130. if (cur->data == X)
  131. {
  132. return cur;
  133. }
  134. cur = cur->next;
  135. }
  136. return NULL;
  137. }
  138. void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType X)//插入-在pos之前插入
  139. {
  140. assert(pphead);
  141. assert(pos);//在pos之前插入
  142. if (pos == *pphead)
  143. {
  144. SListPushFront(pphead,X);
  145. }
  146. else {
  147. SListNode* pre = *pphead;
  148. while (pre->next != pos)
  149. {
  150. pre = pre->next;
  151. assert(pre);//找到NULL还没找到pos说明pos传错了
  152. }
  153. SListNode* newnode = BuyNewNode(X);
  154. pre->next = newnode;
  155. newnode->next = pos;
  156. }
  157. }
  158. void SListEarse(SLTNode** pphead, SLTNode* pos)//删除-删除pos
  159. {
  160. }
  161. void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType X)//插入-在pos之后插入
  162. {
  163. assert(pos);
  164. SListNode* newnode = BuyNewNode(X);
  165. newnode->next = pos->next;
  166. pos->next = newnode;
  167. }

test.cpp

  1. #include"SList.h"
  2. void TestSList1()
  3. {
  4. SLTNode* plist = NULL;
  5. SListPushFront(&plist, 1);
  6. SListPushFront(&plist, 2);
  7. SListPushFront(&plist, 3);
  8. SListPushFront(&plist, 4);
  9. SListPrint(plist);
  10. SLTNode* pos = SListFind(plist, 4);
  11. if (pos)
  12. {
  13. pos->data *= 2;
  14. printf("找到了\n");
  15. }
  16. else
  17. {
  18. printf("没找到\n");
  19. }
  20. SListPrint(plist);
  21. SListInsert(&plist,pos, 20);
  22. SListPrint(plist);
  23. SListDestory(&plist);
  24. SListPrint(plist);
  25. }
  26. int main()
  27. {
  28. TestSList1();
  29. return 0;
  30. }

以上只实现了部分功能,具体可以参考上功能代码实现,或者进到我的链接看噢

https://github.com/divertlee/opening.git


四、总结

本人纯新手昂,有啥问题评论区cue我噢,谢谢观看!!!

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

闽ICP备14008679号