一,引入
1,思考:
本来一个班级20人,定义一个20大小的结构体数组导入新生信息,后来转专业又转来了10个人。如何添加?
1,改代码,增大数组大小
2,找新的储存方式。
3,其它
2,解决办法
若,每个学生信息像下图铁链一样,那就可以随意添加,删除了。这就是链表的思路。
数组是物理上的连续储存。
链表是逻辑上的连续储存
二,如何实现呢?
简陋的实现
创建链表,添加信息
在结构体中加入结构体指针。
第一个程序:如何创建链表
#include<stdio.h>
#include<stdlib.h>
typedef struct student{
char name[20];
student *next;
}s;
int main(){
s s1,s2,s3;
//输入信息
printf("请输入第一个学生的姓名:\n");
scanf("%s",s1.name);
printf("请输入第二个学生的姓名:\n");
scanf("%s",s2.name);
printf("请输入第三个学生的姓名:\n");
scanf("%s",s3.name);
//把三个学生的信息串联到一起
s1.next=&s2;
s2.next=&s3;
s3.next=NULL;
//只使用s1输出所有的信息
printf("第一个学生的名字:%s\n",s1.name);
printf("第二个学生的名字:%s\n",s1.next->name);
printf("第三个学生的名字:%s\n",s1.next->next->name);
return 0;
}
但这个程序太繁琐,这只是添加三个学生的信息,若300呢?如何改进?
看上面的程序,虽然定义了三个结构体名,但输出时只使用了一个变量,这样就显得多余,
解决方法:
1,使用循环,
2,malloc():动态分配内存:
第二个程序:使用循环输入输出链表信息。
虽然代码量比上面的多,若输出的学生信息多就能体现出循环输入的优势了。
#include<stdio.h>
#include<stdlib.h>
typedef struct student{
char name[20];
student *next;
}s;
int main(){
//定义一个带有头节点的链表
s *head=(s*)malloc(sizeof(s));//head:头节点,保存第一个学生变量的地址。
s *p,*tail,*q;//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
}
//遍历输出学生信息。
q=head->next; //q:遍历指针
while(q!=NULL){
int i=1;
printf("第%d个学生名字:%s\n",i,q->name);
q=q->next;//使 q运动,指向下个结构体变量。
}
return 0;
}
对链表删除
上面讲述的是添加链表,那么如何删除信息?
删除函数:free()
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct student{
char name[20];
student *next;
}s;
int main(){
//定义一个带有头节点的链表
s *head=(s*)malloc(sizeof(s));//head:头节点,保存第一个学生变量的地址。
s *p,*tail,*q;//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
}
//遍历输出学生信息。
q=head->next; //q:遍历指针
while(q!=NULL){
int i=1;
printf("第%d个学生名字:%s\n",i,q->name);
q=q->next;//使 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");
}
//再次输出学生信息。
q=head->next; //q:遍历指针
printf("再次输出:\n");
while(q!=NULL){
int i=1;
printf("第%d个学生名字:%s\n",i,q->name);
q=q->next;//使 q运动,指向下个结构体变量。
}
return 0;
}
链表与数组的区别
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站自学,再说课设就没有挑战性了。。。。