当前位置:   article > 正文

C语言—链表_c语言链表

c语言链表

文章目录

一,链表的概念

二,静态创建链表和动态遍历

 三,统计链表节点个数及链表查找

 四,链表的插入

1,从指定节点后方插入新节点

2,从指定节点前方插入新节点 

五,链表删除指定节点

六,动态创建链表

           1,头插法:

           2,尾插法:


一,链表的概念

1,什么是链表?

链表是一种数据结构,是一种数据存放的思想;
2,链表和数组的区别

数组的特点:

  • 数组中的每一个元素都属于同一数据类型的;
  • 数组是一组有序数据的集合;
  • 数组是在内存中开辟一段连续的地址空间用来存放一组数据,可以用数组名加下标来访问数组中的元素; 

链表的特点:               

  • 动态地进行存储分配的一种结构;
  • 链表中的各节点在内存中的地址都是不连续的;
  • 链表是由一个个节点组成,像一条链子一样;
  • 链表中的节点一般包括两个部分(1)用户要用的数据(2)下一个节点的地址;

*两者对比:

  •  一个数组只能存放同一种类型的数据,而链表中就可以存放不同的数据类型;
  • 数组中的元素地址是连续的,想删除或添加一个新的元素,十分的麻烦不灵活,而且用数组存放数据是都要先定义好数组的大小(即元素的个数),如果在定义数组时,定义小了,内存不够用,定义大了,显然会浪费内存;而链表就可以很好的解决这些问题,链表中每一项都是一个结构体,链表中各节点在内存中的地址可以是不连续的,所以你想删除或添加一个新的节点很简单和方便,直接把节点中存放的的地址拿去修改就ok了(具体怎么添加或删除放在后用代码详细讲),因为链表是一种动态结构,所以链表在建立的时候并不用像数组一样需要提前定义大小和位置(具体怎么创建也放在后面用代码详细讲)。

二,静态创建链表和动态遍历

  1. #include<stdio.h>
  2. struct Test//声明结构体类型
  3. {
  4. int data;//定义数据域
  5. struct Test *next;//定义指针域
  6. };
  7. //遍历链表
  8. void printLink(struct Test *head)
  9. {
  10. struct Test *p = head;//使p指向链表头
  11. while(p != NULL){//p现在是链表头,会一直遍历链表尾NULL时,循环结束
  12. printf("%d ",p->data);//输出当前节点中data的值
  13. p = p->next;//使p指向下一个节点
  14. }
  15. printf("\n");
  16. }
  17. int main()
  18. {
  19. struct Test* head;
  20. //定义结构体变量,作为节点,给节点赋值
  21. struct Test t1 = {1,NULL};//对节点t1的data和next赋值
  22. struct Test t2 = {2,NULL};//对节点t2的data和next赋值
  23. struct Test t3 = {3,NULL};//对节点t3的data和next赋值
  24. struct Test t4 = {4,NULL};//对节点t4的data和next赋值
  25. struct Test t5 = {5,NULL};//对节点t5的data和next赋值
  26. head = &t1;//将节点t1的起始地址赋给头指针head
  27. t1.next = &t2;//将节点t2的起始地址赋给t1节点中的next
  28. t2.next = &t3;//将节点t3的起始地址赋给t2节点中的next
  29. t3.next = &t4;//将节点t4的起始地址赋给t3节点中的next
  30. t4.next = &t5;//将节点t5的起始地址赋给t4节点中的next
  31. printLink(head);//把链表头传到函数printLink中
  32. return 0;
  33. }

 三,统计链表节点个数及链表查找

  1. #include<stdio.h>
  2. struct Test//声明结构体类型
  3. {
  4. int data;//定义数据域
  5. struct Test *next;//定义指针域
  6. };
  7. //遍历链表
  8. void printLink(struct Test *head)
  9. {
  10. struct Test *p = head;//使p指向链表头
  11. while(p != NULL){//p现在是链表头,会一直遍历链表尾NULL时,循环停止
  12. printf("%d ",p->data);//输出当前节点中data的值
  13. p = p->next;//使p指向下一个节点
  14. }
  15. }
  16. //统计节点的个数
  17. int getNodeSum(struct Test *head)
  18. {
  19. int sum = 0;
  20. struct Test *p = head;//使p指向链表头
  21. while(p != NULL){//遍历链表,直到链表尾NULL时停止
  22. sum++;//统计链表节点的个数
  23. p = p->next;//使p指向下一个节点
  24. }
  25. return sum;//把统计的节点的个数return回去
  26. }
  27. //链表查询
  28. int searchLink(struct Test *head,int n)
  29. {
  30. while(head != NULL){//遍历链表,直到链表尾NULL时停止
  31. if(head->data == n){//判断当前节点中的data(数据域)和要查询的是否相同
  32. return 1;//相同的话return 1
  33. }
  34. head = head->next;//使head指向下一个节点
  35. }
  36. return 0;//不相同return 0
  37. }
  38. int main()
  39. {
  40. int ret;
  41. struct Test* head;
  42. //定义结构体变量,作为节点,给节点赋值
  43. struct Test t1 = {1,NULL};//对节点t1的data和next赋值
  44. struct Test t2 = {2,NULL};//对节点t2的data和next赋值
  45. struct Test t3 = {3,NULL};//对节点t3的data和next赋值
  46. struct Test t4 = {4,NULL};//对节点t4的data和next赋值
  47. struct Test t5 = {5,NULL};//对节点t5的data和next赋值
  48. head = &t1;//将节点t1的起始地址赋给头指针head
  49. t1.next = &t2;//将节点t2的起始地址赋给t1节点中的next
  50. t2.next = &t3;//将节点t3的起始地址赋给t2节点中的next
  51. t3.next = &t4;//将节点t4的起始地址赋给t3节点中的next
  52. t4.next = &t5;//将节点t5的起始地址赋给t4节点中的next
  53. printLink(head);//把链表头传到函数printLink中
  54. ret = getNodeSum(head);
  55. printf("此链表一共有%d个节点\n",ret);
  56. ret = searchLink(head,2);
  57. if(ret == 1){//判断return回来的值
  58. printf("有1这个节点\n");
  59. }
  60. else{
  61. printf("没有1这个节点\n");
  62. }
  63. ret = searchLink(head,7);
  64. if(ret == 1){//判断return回来的值
  65. printf("有7这个节点\n");
  66. }
  67. else{
  68. printf("没有7这个节点\n");
  69. }
  70. return 0;
  71. }

 四,链表的插入

插入一个新节点有两种方法:        

  1.  在指定节点后
  2.  在指定节点前

1,从指定节点后方插入新节点

找到指定节点,把新节点节点的下一个节点放在要新节点的next(指针域)中,再把新节点放在指定节点的next(指针域)中

  1. #include<stdio.h>
  2. struct Test//声明结构体类型
  3. {
  4. int data;//定义数据域
  5. struct Test *next;//定义指针域
  6. };
  7. //遍历链表
  8. void printLink(struct Test *head)
  9. {
  10. struct Test *p = head;
  11. while(p != NULL){//p现在是链表头,会一直遍历链表尾NULL时停止
  12. printf("%d ",p->data);//输出p节点中data的值
  13. p = p->next;//使p指向下一节点
  14. }
  15. printf("\n");
  16. }
  17. //从指定节点后方插入新节点
  18. void insertFromBehind(struct Test *head,int n,struct Test *new)
  19. {
  20. struct Test *p = head;
  21. while(p != NULL){
  22. if(p->data == n){//判断当前节点是不是指定节点
  23. new->next = p->next;//把新节点的next(指针域)指向指定节点的下一个节点(这边要注意顺序不能换,否则链表会断掉)
  24. p->next = new;//再把指定节点的next(指针域)指向新节点
  25. }
  26. p = p->next;//使p指向下一节点
  27. }
  28. }
  29. int main()
  30. {
  31. struct Test* head;
  32. //定义结构体变量,作为节点,给节点赋值
  33. struct Test t1 = {1,NULL};//对节点t1的data和next赋值
  34. struct Test t2 = {2,NULL};//对节点t2的data和next赋值
  35. struct Test t3 = {3,NULL};//对节点t3的data和next赋值
  36. struct Test t4 = {4,NULL};//对节点t4的data和next赋值
  37. struct Test t5 = {5,NULL};//对节点t5的data和next赋值
  38. head = &t1;//将节点t1的起始地址赋给头指针head
  39. t1.next = &t2;//将节点t2的起始地址赋给t1节点中的next
  40. t2.next = &t3;//将节点t3的起始地址赋给t2节点中的next
  41. t3.next = &t4;//将节点t4的起始地址赋给t3节点中的next
  42. t4.next = &t5;//将节点t5的起始地址赋给t4节点中的next
  43. printLink(head);//把链表头传到函数printLink中
  44. struct Test new = {100,NULL};//定义一个新节点
  45. insertFromBehind(head,5,&new);//把链表头,要插入的位置,和新节点的地址传过去
  46. printLink(head);//把链表头传过去,打印链表
  47. return 0;
  48. }

2,从指定节点前方插入新节点 

要考虑两种情况:

  1. 在第一个节点前插入;
  2. 在中间节点插入;

  1. #include<stdio.h>
  2. struct Test//声明结构体类型
  3. {
  4. int data;//定义数据域
  5. struct Test *next;//定义指针域
  6. };
  7. //遍历链表
  8. void printLink(struct Test *head)
  9. {
  10. struct Test *p = head;
  11. while(p != NULL){//p现在是链表头,会一直遍历链表尾NULL时停止
  12. printf("%d ",p->data);//输出p节点中data的值
  13. p = p->next;//使p指向下一节点
  14. }
  15. printf("\n");
  16. }
  17. //从指定节点前方插入新节点
  18. struct Test* insertFromfront(struct Test *head,int data,struct Test *new)
  19. {
  20. struct Test* p = head;
  21. //在头节点插入(链表头会改变)
  22. if(p->data == data){//判断指定的节点是不是头节点
  23. new->next = p;//让新节点的next(指针域)指向p
  24. return new;//现在new成为新的链表头了
  25. }
  26. //在中间节点插入
  27. while(p->next != NULL){//因为这里是从中间节点插入,所以会从第二个节点开始遍历链表,直到链表尾NULL时停止
  28. if(p->next->data == data){//判断当前节点是不是指定节点
  29. new->next = p->next;//让要插入节点的next(指针域)指向p->next(就是当前节点的下一个节点)
  30. p->next = new;//在让当前节点next(指针域)指向要插入的节点new
  31. return head;//再把链表头给return回去
  32. }
  33. p = p->next;//使p指向下一节点
  34. }
  35. return head;
  36. }
  37. int main()
  38. {
  39. struct Test* head;
  40. //定义结构体变量,作为节点,给节点赋值
  41. struct Test t1 = {1,NULL};//对节点t1的data和next赋值
  42. struct Test t2 = {2,NULL};//对节点t2的data和next赋值
  43. struct Test t3 = {3,NULL};//对节点t3的data和next赋值
  44. struct Test t4 = {4,NULL};//对节点t4的data和next赋值
  45. struct Test t5 = {5,NULL};//对节点t5的data和next赋值
  46. head = &t1;//将节点t1的起始地址赋给头指针head
  47. t1.next = &t2;//将节点t2的起始地址赋给t1节点中的next
  48. t2.next = &t3;//将节点t3的起始地址赋给t2节点中的next
  49. t3.next = &t4;//将节点t4的起始地址赋给t3节点中的next
  50. t4.next = &t5;//将节点t5的起始地址赋给t4节点中的next
  51. printLink(head);//将头指针的地址传到函数printLink中
  52. struct Test new = {100,NULL};//定义一个新节点
  53. head = insertFromfront(head,5,&new);//把链表头,要插入的位置,和新节点的地址传过去
  54. printLink(head);//把链表头传过去,打印链表
  55. return 0;
  56. }

五,链表删除指定节点

要考虑两种情况:

  1. 判断删除的节点是不是第一个节点,如果是第一个节点,直接改链表头,让第二个节点成为新的链表头
  2. 删除的节点如果非第一个节点的话:把要删除节点的前一个节点的next(指针域)越过要删除的节点,然后指向要删除节点的下一个节点;

*注意如果链表是动态创建的记得把删除的这个节点给free掉

  1. #include<stdio.h>
  2. struct Test//声明结构体类型
  3. {
  4. int data;//定义数据域
  5. struct Test *next;//定义指针域
  6. };
  7. //遍历链表
  8. void printLink(struct Test *head)
  9. {
  10. struct Test *p = head;
  11. while(p != NULL){//p现在是链表头,会一直遍历链表尾NULL时停止
  12. printf("%d ",p->data);//输出p节点中data的值
  13. p = p->next;//使p指向下一节点
  14. }
  15. printf("\n");
  16. }
  17. //删除指定节点
  18. struct Test* deleteNode(struct Test *head,int data)
  19. {
  20. struct Test* p = head;
  21. //删除第一个节点
  22. if(p->data == data){//判断要删除的节点是不是头节点
  23. head = head->next;//让p指向下一个节点
  24. return head;//把新的链表头传回去
  25. }
  26. //删除非第一个节点
  27. while(p->next != NULL){//从第二个节点开始遍历链表
  28. if(p->next->data == data){//判断当前节点是不是要删除的节点
  29. p->next = p->next->next;//把要删除节点的前一个节点的next(指针域)越过要删除的节点,然后指向要删除节点的下一个节点
  30. return head;//把链表头传回去
  31. }
  32. p = p->next;//使p指向下一节点
  33. }
  34. return head;
  35. }
  36. int main()
  37. {
  38. struct Test* head;
  39. //定义结构体变量,作为节点,给节点赋值
  40. struct Test t1 = {1,NULL};//对节点t1的data和next赋值
  41. struct Test t2 = {2,NULL};//对节点t2的data和next赋值
  42. struct Test t3 = {3,NULL};//对节点t3的data和next赋值
  43. struct Test t4 = {4,NULL};//对节点t4的data和next赋值
  44. struct Test t5 = {5,NULL};//对节点t5的data和next赋值
  45. head = &t1;//将节点t1的起始地址赋给头指针head
  46. t1.next = &t2;//将节点t2的起始地址赋给t1节点中的next
  47. t2.next = &t3;//将节点t3的起始地址赋给t2节点中的next
  48. t3.next = &t4;//将节点t4的起始地址赋给t3节点中的next
  49. t4.next = &t5;//将节点t5的起始地址赋给t4节点中的next
  50. printLink(head);//将头指针的地址传到函数printLink中
  51. printf("删除指定节点后的链表:\n");
  52. head = deleteNode(head,5);//把链表头,和要删除第几个节点传过去
  53. printLink(head);//把链表头传过去,打印链表
  54. return 0;
  55. }

六,动态创建链表

动态创建链表也是有两种方法:

1,头插法:

每一次创建的新节点插在之前的链表头之前,再让新节点做为新的链表头;

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. struct Test//声明结构体类型
  4. {
  5. int data;//定义数据域
  6. struct Test *next;//定义指针域
  7. };
  8. //遍历链表
  9. void printLink(struct Test *head)
  10. {
  11. struct Test *p = head;
  12. while(p != NULL){//p现在是链表头,会一直遍历链表尾NULL时停止
  13. printf("%d ",p->data);//输出p节点中data的值
  14. p = p->next;//使p指向下一节点
  15. }
  16. printf("\n");
  17. }
  18. struct Test* insertFromHead(struct Test* head,struct Test* new)
  19. {
  20. if(head == NULL){//判断链表是否为空
  21. head = new;//如果为空把创建的新节点当作链表头
  22. }else{
  23. new->next = head;//当链表不为空的时候,让新节点插在链表头的前面(称之为头插法)
  24. head = new;//再让新节点成为链表头
  25. }
  26. return head;
  27. }
  28. //动态创建链表(头插法)
  29. struct Test* creatLink(struct Test* head)
  30. {
  31. struct Test* new;
  32. while(1){
  33. new = (struct Test*)malloc(sizeof(struct Test));//开辟空间
  34. new->next = NULL;
  35. printf("input your ne node data:\n");
  36. scanf("%d",&(new->data));//输入节点的数据域(data)
  37. if(new->data == 0){//判断每次输入的值是否为0,如果为0,停止创建新节点
  38. printf("0 quit\n");
  39. return head;
  40. }
  41. head = insertFromHead(head,new);
  42. }
  43. return head;
  44. }
  45. int main()
  46. {
  47. struct Test* head = NULL;
  48. head = creatLink(head);
  49. printLink(head);
  50. return 0;
  51. }

2,尾插法:

如果链表为空,创建的第一个节点做为链表头,然后每一次创建的新节点插在链表最后一个节点的指针域(next)中;

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. struct Test//声明结构体类型
  4. {
  5. int data;//定义数据域
  6. struct Test *next;//定义指针域
  7. };
  8. //遍历链表
  9. void printLink(struct Test *head)
  10. {
  11. struct Test *p = head;
  12. while(p != NULL){//p现在是链表头,会一直遍历链表尾NULL时停止
  13. printf("%d ",p->data);//输出p节点中data的值
  14. p = p->next;//使p指向下一节点
  15. }
  16. printf("\n");
  17. }
  18. struct Test* insertBehind(struct Test* head,struct Test* new)
  19. {
  20. struct Test* p = head;
  21. if(p == NULL){//判断链表是否为空
  22. return head = new;//如果为空把创建的新节点当作链表头
  23. }
  24. while(p->next != NULL){//遍历到最后一个节点
  25. p = p->next;//使p指向下一节点
  26. }
  27. p->next = new;//把新节点插入最后一个节点的指针域(next)中
  28. return head;//把链表头传回去
  29. }
  30. //动态创建链表(尾插法)
  31. struct Test* creatLink(struct Test* head)
  32. {
  33. struct Test* new;
  34. while(1){
  35. new = (struct Test*)malloc(sizeof(struct Test));//开辟空间
  36. new->next = NULL;
  37. printf("input your ne node data:\n");
  38. scanf("%d",&(new->data));//输入节点的数据域(data)
  39. if(new->data == 0){//判断每次输入的值是否为0,如果为0,停止创建新节点
  40. printf("0 quit\n");
  41. return head;
  42. }
  43. head = insertBehind(head,new);
  44. }
  45. }
  46. int main()
  47. {
  48. struct Test* head = NULL;
  49. head = creatLink(head);
  50. printLink(head);
  51. }

写在最后:

博主是一位在校中专生,刚学不久,实力有限,内容仅供参考,有不对地方欢迎大神指出,一起讨论,一起进步

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

闽ICP备14008679号