最近在复习C语言相关知识,于是手码了个小系统来温习,注释很详细
ps:用结构体实现链表的增删改查的完整操作,如果你刚好也在学习链表或者C指针的内容,希望这篇文章能对你有所帮助。
运行环境:Dev-C++(别笑,Dev老人家老当益壮,正是当打之年...)
一.用到的头文件
其中stdlib.h是malloc()函数的头文件,可以用malloc.h头文件来代替,区别在于stdlib.h是C语言标准库中定义的头文件,库函数比malloc.h多且全面,所以这里建议使用前者。
unistd.h是暂停函数sleep(2)的头文件。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
二.用结构体定义的链表结点
//链表结点定义
struct Stud
{
int no; //学号
int score; //分数
struct Stud *next;
};
三.操作链表的函数
1.链表建立函数
这里建立的是一个单向带头结点的链表。
//1.建立链表
struct Stud *create() //指针型函数
{
struct Stud *h,*t,*p; //t始终指向新建链表的尾结点
int n,s;
h=(struct Stud *)malloc(sizeof(struct Stud)); //*h是头结点
t=h;
printf("输入 0 0退出建表\n");
while(1)
{
printf("请输入 学号 分数:");
scanf("%d%d",&n,&s);
if(n==0 && s==0) break;
else {
p=(struct Stud *)malloc(sizeof(Stud)); //新键结点
p->no=n; p->score=s;
t->next=p; t=p; //上一个结点next指向p,然后t指到p上,整个过程仅用指针操作完成
}
}
t->next=NULL; //将尾结点next域置为NULL
printf("建表完成\n\n");
return h; //返回头结点指针
}
2.查找结点函数
//2.查找结点
struct Stud *locate(struct Stud *h, int n) //查找学号为n的学生结点
{
struct Stud *p=h->next; //用p指向第一个数据结点
while(p!=NULL && p->no!=n)
p=p->next;
return p;
}
3.打印链表函数
//3.打印链表
void disp(struct Stud *h) //传入头结点地址
{
struct Stud *p=h->next; //p指向第一个数据结点
printf("输出链表:");
if(p==NULL) printf("空表\n\n");
else {
while(p!=NULL)
{
printf("(%d %d) ",p->no,p->score);
p=p->next;
}
printf("\n\n");
}
}
4.插入结点函数
p->next=q->next; q->next=p;这句很关键,顺序不能颠倒。
//4.插入结点
void insnode(struct Stud *h,int i,int n,int s) //第i(1<=i<=n)个位置后面插入结点,n对应no,s对应score
{
struct Stud *p,*q; int j;
if(i<1) { printf("插入失败,请输入正确的位置\n\n"); return; } //i超界,返回超界异常
q=h; j=0;
while(q!=NULL && j<i)
{ q=q->next; j++; }
if(q==NULL) { printf("插入失败,请输入正确的位置\n\n"); return; } //i超界,返回超界异常
p=(struct Stud *)malloc(sizeof(struct Stud)); //创建新结点
p->no=n; p->score=s; //数据写入新结点
p->next=q->next; q->next=p; //此时q指向第i个结点,注意插入操作的这两句不可以颠倒顺序,画个图就懂。
{ printf("插入成功\n\n"); return; };
}
5.删除结点函数
单向链表的缺点就是不能走回头路,在删除模块缺点就暴露出来了,我们需要一个额外的指针来记录被删结点的前一个结点。
//5.删除结点
void delnode(struct Stud *h,int n) //删除学号为n的结点
{
struct Stud *p,*q;
q=h;
while(q!=NULL && q->no!=n) {
p=q; q=q->next; //由于是单向链表,所以我们需要一个p指针来记录被删结点的前一个结点
}
if(q==NULL) {
printf("没有学号为%d的学生,请重新输入\n\n",n);
return;
}
else {
p->next=q->next;
free(q); //删除q,释放q的空间
printf("删除成功\n\n");
return;
}
}
6.删除链表函数
删除链表,但保留头结点(doge
//6.删除整个链表
void freelist(struct Stud *h)
{
struct Stud *q=h->next, *p; //删除链表,但保留头结点,后面才能再次进行添加结点等操作
while(q!=NULL)
{
p=q; q=q->next;
free(p);
}
h->next=NULL; //要把头结点的next置为空,这一步很关键!不然会指向一个被释放过的空间造成错误
printf("整条链表已删除\n\n");
}
7.主函数
用了一个while()和switch()来实现多次操作。
int main()
{
struct Stud *a;
a=create(); //建立链表
int u=1; //退出循环的条件
while(u)
{
printf("输入您想做的操作\n 0:查找结点\n 1:打印链表\n 2:插入结点\n 3:删除结点\n 4:删除整条链表\n 5:退出\n 请输入操作编号:");
int p;
scanf("%d",&p);
switch(p)
{
case 0:{ //查找结点
printf("您想查找的学号是:");
int t;
scanf("%d",&t);
struct Stud *k=locate(a,t);
if(k!=NULL) printf("学号为%d的学生的成绩是:%d\n\n",t,k->score);
else printf("抱歉,查无此人\n\n");
break;
}
case 1:{ //打印链表
disp(a);
break;
}
case 2:{ //插入结点
printf("依次输入您想插入的位置、学号、成绩:");
int i,j,k;
scanf("%d%d%d",&i,&j,&k);
insnode(a,i,j,k);
break;
}
case 3:{ //删除结点
printf("您想删除的学生学号为:");
int n;
scanf("%d",&n);
delnode(a,n);
break;
}
case 4:{ //删除整个链表
freelist(a);
break;
}
case 5:{
u=0; //退出循环的条件
printf("已成功退出系统\n");
break;
}
}
sleep(2); //暂停2秒,再进行下一个循环
}
return 0;
}
四.完整代码
最后放一个完整的代码,可以直接复制粘贴在IDE中运行
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h> //暂停函数的头文件
using namespace std;
//链表结点定义
struct Stud
{
int no; //学号
int score; //分数
struct Stud *next;
};
//1.建立链表
struct Stud *create() //指针型函数
{
struct Stud *h,*t,*p; //t始终指向新建链表的尾结点
int n,s;
h=(struct Stud *)malloc(sizeof(struct Stud)); //*h是头结点
t=h;
printf("输入 0 0退出建表\n");
while(1)
{
printf("请输入 学号 分数:");
scanf("%d%d",&n,&s);
if(n==0 && s==0) break;
else {
p=(struct Stud *)malloc(sizeof(Stud)); //新键结点
p->no=n; p->score=s;
t->next=p; t=p; //上一个结点next指向p,然后t指到p上,整个过程仅用指针操作完成
}
}
t->next=NULL; //将尾结点next域置为NULL
printf("建表完成\n\n");
return h; //返回头结点指针
}
//2.查找结点
struct Stud *locate(struct Stud *h, int n) //查找学号为n的学生结点
{
struct Stud *p=h->next; //用p指向第一个数据结点
while(p!=NULL && p->no!=n)
p=p->next;
return p;
}
//3.打印链表
void disp(struct Stud *h) //传入头结点地址
{
struct Stud *p=h->next; //p指向第一个数据结点
printf("输出链表:");
if(p==NULL) printf("空表\n\n");
else {
while(p!=NULL)
{
printf("(%d %d) ",p->no,p->score);
p=p->next;
}
printf("\n\n");
}
}
//4.插入结点
void insnode(struct Stud *h,int i,int n,int s) //第i(1<=i<=n)个位置后面插入结点,n对应no,s对应score
{
struct Stud *p,*q; int j;
if(i<1) { printf("插入失败,请输入正确的位置\n\n"); return; } //i超界,返回超界异常
q=h; j=0;
while(q!=NULL && j<i)
{ q=q->next; j++; }
if(q==NULL) { printf("插入失败,请输入正确的位置\n\n"); return; } //i超界,返回超界异常
p=(struct Stud *)malloc(sizeof(struct Stud)); //创建新结点
p->no=n; p->score=s; //数据写入新结点
p->next=q->next; q->next=p; //此时q指向第i个结点,注意插入操作的这两句不可以颠倒顺序,画个图就懂。
{ printf("插入成功\n\n"); return; };
}
//5.删除结点
void delnode(struct Stud *h,int n) //删除学号为n的结点
{
struct Stud *p,*q;
q=h;
while(q!=NULL && q->no!=n) {
p=q; q=q->next; //由于是单向链表,所以我们需要一个p指针来记录被删结点的前一个结点
}
if(q==NULL) {
printf("没有学号为%d的学生,请重新输入\n\n",n);
return;
}
else {
p->next=q->next;
free(q); //删除q,释放q的空间
printf("删除成功\n\n");
return;
}
}
//6.删除整个链表
void freelist(struct Stud *h)
{
struct Stud *q=h->next, *p; //删除链表,但保留头结点,后面才能再次进行添加结点等操作
while(q!=NULL)
{
p=q; q=q->next;
free(p);
}
h->next=NULL; //要把头结点的next置为空,这一步很关键!不然会指向一个被释放过的空间造成错误
printf("整条链表已删除\n\n");
}
int main()
{
struct Stud *a;
a=create(); //建立链表
int u=1; //退出循环的条件
while(u)
{
printf("输入您想做的操作\n 0:查找结点\n 1:打印链表\n 2:插入结点\n 3:删除结点\n 4:删除整条链表\n 5:退出\n 请输入操作编号:");
int p;
scanf("%d",&p);
switch(p)
{
case 0:{ //查找结点
printf("您想查找的学号是:");
int t;
scanf("%d",&t);
struct Stud *k=locate(a,t);
if(k!=NULL) printf("学号为%d的学生的成绩是:%d\n\n",t,k->score);
else printf("抱歉,查无此人\n\n");
break;
}
case 1:{ //打印链表
disp(a);
break;
}
case 2:{ //插入结点
printf("依次输入您想插入的位置、学号、成绩:");
int i,j,k;
scanf("%d%d%d",&i,&j,&k);
insnode(a,i,j,k);
break;
}
case 3:{ //删除结点
printf("您想删除的学生学号为:");
int n;
scanf("%d",&n);
delnode(a,n);
break;
}
case 4:{ //删除整个链表
freelist(a);
break;
}
case 5:{
u=0; //退出循环的条件
printf("已成功退出系统\n");
break;
}
}
sleep(2); //暂停2秒,再进行下一个循环
}
return 0;
}
谢谢阅读!