当前位置:   article > 正文

链表——删除元素或插入元素(头插法及尾插法)_链表插入元素

链表插入元素

目录

链表的结点由一个结构体构成

判断链表是否为空

键盘输入链表中的数据

输出链表中的数据

返回链表的元素个数

清空链表

 返回指定位置的元素值

查找数据所在位置

删除链表的元素

插入元素

建立无头结点的单链表

建立有头结点的单链表(头插法)

建立有头节点的单链表(尾插法)

总代码如下:


链表:

是一种在存储单元上非连续,非顺序的存储结构,由一系列结点组成,结点可以在运行的过程中动态生成,每个结点包括两部分:存储数据的数据域;存储下一节点地址的指针域

指针的理解:

将某个变量(例如 int a)赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。

链表的结点由一个结构体构成

  1. struct node
  2. {
  3. int data;//数据域
  4. struct node *next;//指针域
  5. };

判断链表是否为空

如果链表头结点的指针域不为空,如果定义链表头结点是 struct node *L,也就是 L->next !=NULL.

(因为在有头结点的链表中,头结点数据域中不存储数据,它的指针域存放的是链表第一个元素的地址)

  1. //判断链表是否为空
  2. bool isEmptyList(struct node* L)
  3. {
  4. //判断头结点指向的下一指针域不为空,说明该链表不为空
  5. if (L->next != NULL)
  6. return true;//不为空
  7. else
  8. return false;//为空
  9. }

建立头结点:

创建头结点,为头结点分配内存(用到 malloc 函数,使用头文件),令链表的头指针为空指针(有头节点的链表)。

键盘输入链表中的数据

  1. //输入n个元素
  2. void putList(struct node *L, int n)
  3. {
  4. struct node* p, * q;
  5. L->next = (struct node*)malloc(sizeof(struct node));
  6. q = L->next;
  7. printf("输入%d个元素放入链表:",n);
  8. for (int i = 0; i < n; i++)
  9. {
  10. int x;
  11. scanf("%d", &x);
  12. p = (struct node*)malloc(sizeof(struct node));
  13. p->data = x;
  14. p->next = NULL;
  15. q->next = p;
  16. q = p;
  17. }
  18. }

输出链表中的数据

  1. //输出链表中的数据
  2. void printList(struct node* L)
  3. {
  4. struct node* p;
  5. p = L->next;
  6. printf("当前链表元素:");
  7. while (p->next != NULL)
  8. {
  9. p = p->next;//工作指针后移
  10. printf("%d ", p->data);//输出当前结点的数据
  11. }
  12. printf("\n");
  13. }

返回链表的元素个数

因为链表是动态分配内存,所以没有记录结点个数,此时可以从头结点开始,通过指针后移找到后面的结点,当指针域为空时,就结束循环,循环的次数就是结点个数。

  1. //返回链表元素个数
  2. int numList(struct node* L)
  3. {
  4. int sum = 0;//计数器
  5. struct node* p;//定义一个工作指针,因为如果直接用L指针后移的话,这个链表就找不到了
  6. p = L->next;//将头指针指针域指向的地址赋给p,也就是链表的第一个元素(头指针中不存放数据)
  7. while (p != NULL)
  8. {
  9. p=p->next;
  10. sum++;
  11. }
  12. return sum;
  13. }

清空链表

对于清空链表,是要把链表中每一个结点的空间都释放,而不是只释放头结点的空间。而且在释放空间之前,要记录它的指针域。

  1. //清空链表
  2. void clearList(struct node** L)
  3. {
  4. struct node* p, * q;
  5. p = (*L)->next;//工作指针
  6. while (p!=NULL)
  7. {
  8. //记录将要被释放空间的结点的指针域,如果不记录,那么链表后面的结点就找不到了
  9. q = p->next;
  10. free(p);
  11. p = q;
  12. }
  13. (*L)->next = NULL;
  14. }

 返回指定位置的元素值

用一个工作指针后移找结点,循环时计数器自增,找到指定位置,然后返回。

  1. //返回链表的指定位置的元素
  2. int elementList(struct node* L, int x)//x为指定位置
  3. {
  4. int i = 0;//计数器
  5. struct node* p;
  6. p = L->next;
  7. //找到指定位置的前面那个位置,因为当刚好i==x-1时,此时进入循环,p=p->next,刚好到达结点为x的位置
  8. while (p != NULL && i < x-1)
  9. {
  10. p = p->next;
  11. i++;
  12. }
  13. if (p)
  14. return p->data;
  15. else
  16. return 0;
  17. }

查找数据所在位置

用一个工作指针后移找结点,循环中计数器自增,如果找到一个结点的数据与目标值相等就返回该位置,直到循环结束,若是没有返回,就说明链表里面没有 x 元素。

  1. //查找数据所在位置
  2. int findList(struct node* L, int x)
  3. {
  4. int i = 0;
  5. struct node* p;
  6. p = L->next;
  7. while (p != NULL)
  8. {
  9. if (p->data == x)//找到了直接输出该节点的序号
  10. return i;
  11. i++;
  12. p = p->next;
  13. }
  14. return 0;//代表没有找到数据为x的结点
  15. }

也有更好的方法,比如说如果有多个结点的元素与 x 相等,上面这个代码就解决不了了,此时可以用一个数组记录找到的位置,结束时返回该数组的地址。

删除链表的元素

先搞清有头结点的链表和无头结点的链表之间的区别:

无头结点的单链表_没有头结点的循环单链表-CSDN博客

对于有头结点的链表就相对来说方便一些,不用考虑删除头指针的情况。

将要删除的结点赋给q,然后将 p->next 赋给 p->next.

  1. //删除元素
  2. bool deleteList(struct node* L, int x, int* e)//x是删除元素的结点序号,传入的地址e是用来记录删除结点的数据的
  3. {
  4. struct node* p, * q;
  5. p = L->next;
  6. int i = 0;
  7. while (i < x - 2 && p->next != NULL)//这里循环到i<x-1,因为找到当前的结点后,是删除它后面那个元素
  8. {
  9. p = p->next;
  10. i++;
  11. }
  12. if (p->next == NULL)
  13. return false;//删除失败
  14. q = p->next;
  15. *e = q->data;
  16. p->next = q->next;
  17. return true;//删除成功
  18. }

插入元素

首先插入键盘输入的数字作为插入的数据:

先开辟一块空间,再将值放入 p 的数据域,将其指针域赋为 NULL,每次循环都进行此操作(到了最后一个结点的时候,它的指针域就是 NULL),也可以在输入完数据后将最后一个结点的指针域赋为 NULL。

每次循环将 p 结点赋给 q 结点,下一轮循环时就将 q->next=p.

  1. //插入元素(输入)
  2. void PutList(struct node* L,int x,int y)
  3. {
  4. struct node* q,* p,* s;
  5. int i=1;
  6. p = L;
  7. while (i < x && p != NULL)//找到要插入元素的位置
  8. {
  9. p = p->next;
  10. i++;
  11. }
  12. s = (struct node*)malloc(sizeof(struct node));
  13. s->data = y;//赋值
  14. //这两步不能反
  15. s->next = p->next;
  16. p->next = s;
  17. }

也可以生成随机数据作为插入的数据:此时用到 rand 函数和 srand 函数(用到 #include<stdlib.h> 函数和 #include<time.h> 头文件)

  1. //插入元素(随机)
  2. void PutRandList(struct node** L,int x)
  3. {
  4. struct node* p, * q;
  5. q = NULL;
  6. int i = 1;
  7. p = (*L);
  8. while (i < x && p != NULL)
  9. {
  10. p = p->next;
  11. i++;
  12. }
  13. srand(time(0));
  14. int s = rand()%100;
  15. q = (struct node*)malloc(sizeof(struct node));
  16. q->data = s;
  17. q->next = p->next;
  18. p->next = q;
  19. }

建立无头结点的单链表

链表的每一个结点都有数据域,说明首元结点前没有头结点。

  1. //创建链表(无头结点)
  2. void creatList(struct node** L, int n)
  3. {
  4. printf("输入%d个元素:",n);
  5. struct node* p, * q;
  6. *L = NULL;
  7. for (int i = 0; i < n; i++)
  8. {
  9. int x;
  10. scanf("%d", &x);
  11. p = (struct node*)malloc(sizeof(struct node));
  12. p->data = x;
  13. p->next = NULL;
  14. if (*L == NULL)//如果头结点是空的就将第一个结点赋值给头结点(因为这是无头结点)
  15. *L = p;
  16. else
  17. q->next = p;
  18. q = p;
  19. }
  20. }

建立有头结点的单链表(头插法)

建立一个带头结点的链表,为结点 p 开辟一块空间,然后将生成的随机数赋值给 p 的数据域,将 p 插入到表头,循环执行。

  1. //建立有头结点的单链表(头插法)
  2. void CreateListHead(struct node* L, int n)//n为元素个数
  3. {
  4. srand(time(0));
  5. struct node* p;
  6. for(int i=0;i<n;i++)
  7. {
  8. p = (struct node*)malloc(sizeof(struct node));
  9. int s = rand() % 100;//随机数
  10. p->data = s;
  11. p->next = L->next;
  12. L->next = p;
  13. }
  14. }

建立有头节点的单链表(尾插法)

与头插法不同的是:尾插法需要另外一个指向尾部的结点 r ,在链表中插入元素时,只需要将 r 的指针指向 p 即可,然后将 p 赋值给 r ,这样可以使 r 始终在链表尾部,并且将要插入的元素置于 r 的后方,也就是链表的尾部。插入结束后,将链表尾部的元素的指针指向NULL。

  1. //建立有头结点的单链表(尾插法)
  2. void CreateListTail(struct node* L, int n)
  3. {
  4. srand(time(0));
  5. struct node* p, * q;
  6. q = L;
  7. for (int i = 0; i < n; i++)
  8. {
  9. p = (struct node*)malloc(sizeof(struct node));
  10. int s = rand() % 100;
  11. p->data = s;
  12. q->next = p;
  13. q = p;
  14. }
  15. q->next = NULL;
  16. }

总代码如下:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<time.h>
  4. struct node
  5. {
  6. int data;
  7. struct node* next;
  8. };
  9. //清空链表
  10. void clearList(struct node** L)
  11. {
  12. struct node* p, * q;
  13. p = (*L)->next;//工作指针
  14. while (p != NULL)
  15. {
  16. //记录将要被释放空间的结点的指针域,如果不记录,那么链表后面的结点就找不到了
  17. q = p->next;
  18. free(p);
  19. p = q;
  20. }
  21. (*L)->next = NULL;
  22. }
  23. //输入n个元素
  24. void putList(struct node* L, int n)
  25. {
  26. struct node* p, * q;
  27. L->next = (struct node*)malloc(sizeof(struct node));
  28. q = L;
  29. printf("输入%d个元素放入链表:", n);
  30. for (int i = 0; i < n; i++)
  31. {
  32. int x;
  33. scanf("%d", &x);
  34. p = (struct node*)malloc(sizeof(struct node));
  35. p->data = x;
  36. p->next = NULL;
  37. q->next = p;
  38. q = p;
  39. }
  40. }
  41. //输出链表中的数据
  42. void printList(struct node* L)
  43. {
  44. struct node* p;
  45. p = L->next;
  46. printf("当前链表元素:");
  47. while (p != NULL)
  48. {
  49. printf("%d ", p->data);//输出当前结点的数据
  50. p = p->next;//工作指针后移
  51. }
  52. printf("\n");
  53. }
  54. //返回链表元素个数
  55. int numList(struct node* L)
  56. {
  57. int sum = 0;//计数器
  58. struct node* p;//定义一个工作指针,因为如果直接用L指针后移的话,这个链表就找不到了
  59. p = L->next;//将头指针指针域指向的地址赋给p,也就是链表的第一个元素(头指针中不存放数据)
  60. while (p != NULL)
  61. {
  62. p = p->next;
  63. sum++;
  64. }
  65. return sum;
  66. }
  67. //判断链表是否为空
  68. bool isEmptyList(struct node* L)
  69. {
  70. //判断头结点指向的下一指针域不为空,说明该链表不为空
  71. if (L->next != NULL)
  72. return true;//不为空
  73. else
  74. return false;//为空
  75. }
  76. //初始化
  77. void InitList(struct node** L)
  78. {
  79. *L = (struct node*)malloc(sizeof(struct node));
  80. (*L)->next = NULL;
  81. }
  82. //返回链表的指定位置的元素
  83. int elementList(struct node* L, int x)//x为指定位置
  84. {
  85. int i = 0;//计数器
  86. struct node* p;
  87. p = L->next;
  88. //找到指定位置的前面那个位置,因为当刚好i==x-1时,此时进入循环,p=p->next,刚好到达结点为x的位置
  89. while (p != NULL && i < x-1)
  90. {
  91. p = p->next;
  92. i++;
  93. }
  94. if (p)
  95. return p->data;
  96. else
  97. return 0;
  98. }
  99. //查找数据所在位置
  100. int findList(struct node* L, int x)
  101. {
  102. int i = 0;
  103. struct node* p;
  104. p = L;
  105. while (p != NULL)
  106. {
  107. if (p->data == x)//找到了直接输出该节点的序号
  108. return i;
  109. i++;
  110. p = p->next;
  111. }
  112. return 0;//代表没有找到数据为x的结点
  113. }
  114. //删除元素
  115. bool deleteList(struct node* L, int x, int* e)//x是删除元素的结点序号,传入的地址e是用来记录删除结点的数据的
  116. {
  117. struct node* p, * q;
  118. p = L->next;
  119. int i = 0;
  120. while (i < x - 2 && p->next != NULL)//这里循环到i<x-1,因为找到当前的结点后,是删除它后面那个元素
  121. {
  122. p = p->next;
  123. i++;
  124. }
  125. if (p->next == NULL)
  126. return false;//删除失败
  127. q = p->next;
  128. *e = q->data;
  129. p->next = q->next;
  130. return true;//删除成功
  131. }
  132. //插入元素(输入)
  133. void PutList(struct node* L, int x, int y)
  134. {
  135. struct node* q, * p, * s;
  136. int i = 1;
  137. p = L;
  138. while (i < x && p != NULL)//找到要插入元素的位置
  139. {
  140. p = p->next;
  141. i++;
  142. }
  143. s = (struct node*)malloc(sizeof(struct node));
  144. s->data = y;//赋值
  145. //这两步不能反
  146. s->next = p->next;
  147. p->next = s;
  148. }
  149. //插入元素(随机)
  150. void PutRandList(struct node** L, int x)
  151. {
  152. struct node* p, * q;
  153. q = NULL;
  154. int i = 1;
  155. p = (*L);
  156. while (i < x && p != NULL)
  157. {
  158. p = p->next;
  159. i++;
  160. }
  161. srand(time(0));
  162. int s = rand() % 100;
  163. q = (struct node*)malloc(sizeof(struct node));
  164. q->data = s;
  165. q->next = p->next;
  166. p->next = q;
  167. }
  168. //创建链表(无头结点)
  169. void creatList(struct node** L, int n)
  170. {
  171. printf("输入%d个元素:", n);
  172. struct node* p, * q;
  173. *L = NULL;
  174. for (int i = 0; i < n; i++)
  175. {
  176. int x;
  177. scanf("%d", &x);
  178. p = (struct node*)malloc(sizeof(struct node));
  179. p->data = x;
  180. p->next = NULL;
  181. if (*L == NULL)//如果头结点是空的就将第一个结点赋值给头结点(因为这是无头结点)
  182. *L = p;
  183. else
  184. q->next = p;
  185. q = p;
  186. }
  187. }
  188. //建立有头结点的单链表(头插法)
  189. void CreateListHead(struct node* L, int n)//n为元素个数
  190. {
  191. srand(time(0));
  192. struct node* p;
  193. for (int i = 0; i < n; i++)
  194. {
  195. p = (struct node*)malloc(sizeof(struct node));
  196. int s = rand() % 100;//随机数
  197. p->data = s;
  198. p->next = L->next;
  199. L->next = p;
  200. }
  201. }
  202. //建立有头结点的单链表(尾插法)
  203. void CreateListTail(struct node* L, int n)
  204. {
  205. srand(time(0));
  206. struct node* p, * q;
  207. q = L;
  208. for (int i = 0; i < n; i++)
  209. {
  210. p = (struct node*)malloc(sizeof(struct node));
  211. int s = rand() % 100;
  212. p->data = s;
  213. q->next = p;
  214. q = p;
  215. }
  216. //这个地方不能省,因为输出的时候是以最后一个结点的指针域为NULL判断结束的
  217. q->next = NULL;
  218. }
  219. int main()
  220. {
  221. int n, x;
  222. struct node* L;
  223. //初始化后判断链表是否为空
  224. InitList(&L);//初始化链表
  225. if (isEmptyList(L))
  226. printf("当前链表不为空\n");
  227. else
  228. printf("当前链表为空\n");
  229. //输入n个元素存入链表
  230. printf("输入元素个数:");
  231. scanf("%d", &n);//输入元素个数
  232. putList(L, n);//键盘输入n个元素进入链表
  233. printList(L);
  234. if (isEmptyList(L))
  235. printf("当前链表不为空\n");
  236. else
  237. printf("当前链表为空\n");
  238. //统计链表中元素个数
  239. printf("链表中元素个数:%d\n", numList(L));
  240. //输入链表的结点位置,找对应位置的元素
  241. printf("输入要查找的地址:");
  242. scanf("%d", &x);
  243. printf("%d位置上的元素是:%d\n", x, elementList(L, x));
  244. //输入元素,找链表中对应的位置
  245. printf("输入要查找的数据:");
  246. scanf("%d", &x);
  247. printf("%d在链表的第%d个\n", x, findList(L, x));
  248. //输入要删除的位置,删除对应的元素
  249. printf("输入要删除的位置:");
  250. scanf("%d", &x);
  251. int y;//记录删除的那个元素是什么
  252. if (deleteList(L, x, &y))
  253. {
  254. printList(L);//打印出链表
  255. printf("删除的元素是:%d\n", y);
  256. }
  257. else
  258. printf("删除失败\n");
  259. //插入元素
  260. printf("输入插入的元素的位置和元素:");
  261. scanf("%d%d", &x, &y);
  262. PutList(L, x, y);
  263. printList(L);//打印出链表
  264. //插入元素(随机生成数)
  265. printf("输入要插入随机数字的位置:");
  266. scanf("%d", &x);
  267. PutRandList(&L, x);
  268. printList(L);//打印出链表
  269. clearList(&L);//清空链表
  270. if (isEmptyList(L))//判断链表是否为空
  271. printf("当前链表不为空\n");
  272. else
  273. printf("当前链表为空\n");
  274. //建立无头结点的链表
  275. printf("建立无头结点的单链表,输入结点个数:");
  276. scanf("%d", &x);
  277. creatList(&L, x);//建立无头结点的单链表
  278. struct node* t = L;
  279. while (t != NULL)//打印无头结点单链表(打印时不能用上面的printList函数,因为这个是无头结点的链表)
  280. {
  281. printf("%d ", t->data);
  282. t = t->next;
  283. }
  284. printf("\n");
  285. clearList(&L);//清空
  286. //建立有头结点的单链表
  287. printf("输入头插法的要插入的元素个数:");
  288. scanf("%d", &x);
  289. CreateListHead(L, x);
  290. printList(L);
  291. clearList(&L);//清空
  292. //建立有头结点的单链表(随机生成数)
  293. printf("输入尾插法的要插入的元素个数:");
  294. scanf("%d", &x);
  295. CreateListTail(L, x);
  296. printList(L);
  297. clearList(&L);//清空
  298. }

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

闽ICP备14008679号