C语言实现链表相关操作

最近在复习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;
}

谢谢阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值