一,引入

1,思考:

本来一个班级20人,定义一个20大小的结构体数组导入新生信息,后来转专业又转来了10个人。如何添加?
1,改代码,增大数组大小
2,找新的储存方式。
3,其它

2,解决办法

若,每个学生信息像下图铁链一样,那就可以随意添加,删除了。这就是链表的思路。
数组是物理上的连续储存。
链表是逻辑上的连续储存
image.png

二,如何实现呢?

简陋的实现

创建链表,添加信息

在结构体中加入结构体指针。
image.png
第一个程序:如何创建链表

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. typedef struct student{
  4. char name[20];
  5. student *next;
  6. }s;
  7. int main(){
  8. s s1,s2,s3;
  9. //输入信息
  10. printf("请输入第一个学生的姓名:\n");
  11. scanf("%s",s1.name);
  12. printf("请输入第二个学生的姓名:\n");
  13. scanf("%s",s2.name);
  14. printf("请输入第三个学生的姓名:\n");
  15. scanf("%s",s3.name);
  16. //把三个学生的信息串联到一起
  17. s1.next=&s2;
  18. s2.next=&s3;
  19. s3.next=NULL;
  20. //只使用s1输出所有的信息
  21. printf("第一个学生的名字:%s\n",s1.name);
  22. printf("第二个学生的名字:%s\n",s1.next->name);
  23. printf("第三个学生的名字:%s\n",s1.next->next->name);
  24. return 0;
  25. }

但这个程序太繁琐,这只是添加三个学生的信息,若300呢?如何改进?
看上面的程序,虽然定义了三个结构体名,但输出时只使用了一个变量,这样就显得多余,
解决方法:
1,使用循环,
2,malloc():动态分配内存:
第二个程序:使用循环输入输出链表信息。
虽然代码量比上面的多,若输出的学生信息多就能体现出循环输入的优势了。

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. typedef struct student{
  4. char name[20];
  5. student *next;
  6. }s;
  7. int main(){
  8. //定义一个带有头节点的链表
  9. s *head=(s*)malloc(sizeof(s));//head:头节点,保存第一个学生变量的地址。
  10. s *p,*tail,*q;//p:动态分配结构体无名变量的空间
  11. //tail:保存最后一个学生变量的地址。
  12. //循环输入学生信息
  13. for(int i=0;i<3;i++){
  14. p=(s*)malloc(sizeof(s));
  15. printf("第%d个学生姓名:\n",i+1);
  16. scanf("%s",p->name);
  17. p->next=NULL;
  18. if(i==0){
  19. head->next=p;//注意,定义的p是无名变量,注意地址的保存
  20. //保存第一个学生信息的地址。
  21. }
  22. else{
  23. tail->next=p;//在链表末尾插入新的学生变量
  24. }
  25. tail=p;//更新tail
  26. }
  27. //遍历输出学生信息。
  28. q=head->next; //q:遍历指针
  29. while(q!=NULL){
  30. int i=1;
  31. printf("第%d个学生名字:%s\n",i,q->name);
  32. q=q->next;//使 q运动,指向下个结构体变量。
  33. }
  34. return 0;
  35. }

对链表删除

上面讲述的是添加链表,那么如何删除信息?
删除函数:free()

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. typedef struct student{
  5. char name[20];
  6. student *next;
  7. }s;
  8. int main(){
  9. //定义一个带有头节点的链表
  10. s *head=(s*)malloc(sizeof(s));//head:头节点,保存第一个学生变量的地址。
  11. s *p,*tail,*q;//p:动态分配结构体无名变量的空间
  12. //tail:保存最后一个学生变量的地址。
  13. //循环输入学生信息
  14. for(int i=0;i<3;i++){
  15. p=(s*)malloc(sizeof(s));
  16. printf("第%d个学生姓名:\n",i+1);
  17. scanf("%s",p->name);
  18. p->next=NULL;
  19. if(i==0){
  20. head->next=p;//注意,定义的p是无名变量,注意地址的保存
  21. //保存第一个学生信息的地址。
  22. }
  23. else{
  24. tail->next=p;//在链表末尾插入新的学生变量
  25. }
  26. tail=p;//更新tail
  27. }
  28. //遍历输出学生信息。
  29. q=head->next; //q:遍历指针
  30. while(q!=NULL){
  31. int i=1;
  32. printf("第%d个学生名字:%s\n",i,q->name);
  33. q=q->next;//使 q运动,指向下个结构体变量。
  34. }
  35. //删除信息
  36. printf("请输入要删除的学生名字:\n");
  37. char name[20];
  38. scanf("%s",name);
  39. p=head; //保证p始终指向q上个节点
  40. q=head->next;//q指向要查找信息的节点
  41. while(q!=NULL){
  42. if(strcmp(q->name,name)==0){
  43. printf("查找到了:%s\n",q->name);
  44. p->next=q->next; //把要删除的节点储存的地址值赋值给上个节点。
  45. p=q;//因为找到目标,并且链表已经更改,所以p无意义了
  46. free(p);
  47. p=NULL;
  48. printf("删除成功!\n");
  49. break;
  50. }
  51. p=p->next;
  52. q=q->next;
  53. }
  54. if(q==NULL){
  55. printf("没有找到!\n");
  56. }
  57. //再次输出学生信息。
  58. q=head->next; //q:遍历指针
  59. printf("再次输出:\n");
  60. while(q!=NULL){
  61. int i=1;
  62. printf("第%d个学生名字:%s\n",i,q->name);
  63. q=q->next;//使 q运动,指向下个结构体变量。
  64. }
  65. return 0;
  66. }

链表与数组的区别
1,链表添加,删除,方便。查找不便。
2,数组查找方便。添加,删除不便利。

好看的程序-模块化

模块化 程序设计 是指在进行 程序设计 时将一个大程序按照功能划分为若干小程序模块,每个小程序模块完成一个确定的功能,并在这些模块之间建立必要的联系,通过模块的互相协作完成整个功能的程序设计方法。

优点:

1)控制了程序设计的复杂性。
(2)提高了代码的重用性。
(3)易于维护和功能扩充。
(4)有利于团队开发。
把上面程序模块化:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct student{
    char name[20];
    student *next;
}s;
void add(s *head){
        s *p,*tail;//p:动态分配结构体无名变量的空间
                //tail:保存最后一个学生变量的地址。

    //循环输入学生信息 
    for(int i=0;i<3;i++){
        p=(s*)malloc(sizeof(s));
        printf("第%d个学生姓名:\n",i+1);
        scanf("%s",p->name);
        p->next=NULL;
        if(i==0){
            head->next=p;//注意,定义的p是无名变量,注意地址的保存 
                    //保存第一个学生信息的地址。 
        }
        else{
            tail->next=p;//在链表末尾插入新的学生变量    
        }
        tail=p;//更新tail 
    }
} 
void show(s *head){
    //遍历输出学生信息。
    s *q;
    q=head->next; //q:遍历指针  
    while(q!=NULL){
        int i=1;
        printf("第%d个学生名字:%s\n",i,q->name);
        q=q->next;//使 q运动,指向下个结构体变量。 
    }
}
void del(s *head){
    s *p,*q;
            //删除信息 
    printf("请输入要删除的学生名字:\n");
    char name[20];
    scanf("%s",name);
    p=head; //保证p始终指向q上个节点 
    q=head->next;//q指向要查找信息的节点 
    while(q!=NULL){
        if(strcmp(q->name,name)==0){
            printf("查找到了:%s\n",q->name);
            p->next=q->next; //把要删除的节点储存的地址值赋值给上个节点。
            p=q;//因为找到目标,并且链表已经更改,所以p无意义了 
            free(p);
            p=NULL;
            printf("删除成功!\n");
            break;
        }
        p=p->next;
        q=q->next; 
    }
    if(q==NULL){
        printf("没有找到!\n"); 
    }
}
int main(){
    //定义一个带有头节点的链表 
    s *head=(s*)malloc(sizeof(s));//head:头节点,保存第一个学生变量的地址。
add(head);
show(head);
del(head);
show(head);
    return 0;
}

对于链表排序,是个重要的算法。b站自学,再说课设就没有挑战性了。。。。