链表--学生信息管理

链表结构

  • 逻辑结构:线性表
  • 存储结构:链式存储
  • 基本运算:
    1. 初始化
    2. 查找节点
    3. 插入节点
    4. 删除节点
    5. 创建单链表

准备数据

其中,节点包含两部分的内容:
数据域: 保存该节点的实际数据。
指针域: 保存的是下一节点的地址。

struct Student			//学生结构
{
	char Name[10];		//姓名
	int No;				//学号
	float Score[3];		//三科的成绩
	float Total;		//总分
	float Ave;			//平均分
};

typedef struct Node		//结点
{
	struct Student st;	//数据域
	struct Node *next;	//指针域
}NODE, *PNODE;			//NODE等价于struct Student st||PNODE等价于struct Node *next
						//NODE * P==PNODE P。为了提高可读性用NODE定义指向其他任意节点。用PNODE专指向头结点 

初始化

初始化一个头结点,它不存储学生信息,也可以不存任何信息,也可以存附加信息,如存表长。它的指针域指向第一个节点。之所以引入头结点,就是为了方便,即使不用头结点,也能完成相同的功能。

PNODE InitList(PNODE pHead)
{
	PNODE p=pHead;
	p = (PNODE)malloc(sizeof(NODE));//为头结点分配内存
	if(NULL == p)	//判断内存是否为空
	{
		printf("内存分配失败,程序终止!\n");
		exit(-1);
	}
	p->next = NULL;//清空指针域
	return p;
}

查找

查找可以按不同的域去查找,这里以按姓名查找为例,核心代码都一样。

void SearchStudent(PNODE pHead)			//查找学生信息
{
	char Name[10];
	printf("请输入你需要查找的学生的姓名:");
	scanf("%s",Name);
	PNODE p = pHead->next;				//p指向第一个节点 
	while(p)
	{
		if(0 == strcmp(Name,p->st.Name))
		{
			printf("%s %d %g %g %g %g %g\n",p->st.Name, p->st.No, p->st.Score[0], p->st.Score[1], p->st.Score[2], p->st.Total, p->st.Ave);
		}
		p = p->next;
	}
}

插入

void InsertStudent(PNODE pHead)		//插入学生信息
{
	PNODE p = pHead;
	int i = 0;
	struct Student stu;				//学生结构,为数据域
	int pos;						//插入结点的位置
	printf("请输入插入学生的位置:");
	scanf("%d",&pos);
	while(p && i<pos-1)
	{
		p = p->next;
		i++;
	}
	if(p == NULL || i>pos)
	{
		printf("插入结点的位置不存在!\n");
		return;
	}
	printf("你将在第%d个学生后面插入一个学生\n",pos-1);
	printf("请输入第%d个学生的姓名:",pos);
	scanf("%s",stu.Name);
	printf("请输入第%d个学生的学号:",pos);
	scanf("%d",&stu.No);
	printf("请输入第%d个学生的语文成绩:",pos);
	scanf("%f",&stu.Score[0]);
	printf("请输入第%d个学生的数学成绩:",pos);
	scanf("%f",&stu.Score[1]);
	printf("请输入第%d个学生的英语成绩:",pos);
	scanf("%f",&stu.Score[2]);

	stu.Total = stu.Score[0] + stu.Score[1] + stu.Score[2];//计算总分	
	stu.Ave = stu.Total / 3.0f;//计算平均分
	PNODE pNew = (PNODE)malloc(sizeof(NODE));
	if(NULL == pNew)
	{
		printf("动态内存分配失败,程序终止!\n");
		exit(-1);
	}
	pNew->st = stu;
	PNODE q = p->next;			//先把后继存起来 
	p->next = pNew;				//然后把要插的插进来 
	pNew->next = q;				//将刚刚保存的节点挂上 
}

删除

删除与插入其实差不多,主要就是改变指针的指向,使其指向合适的位置即可。

void DeleteStudent(PNODE pHead)				//删除学生信息
{
	PNODE p = pHead;
	int i = 0;
	int pos;
	printf("请输入你需要删除的学生的编号:");
	scanf("%d",&pos);
	while(p && i<pos-1)
	{
		p = p->next;
		i++;
	}

	if(p == NULL || i>pos-1)
	{
		printf("没找到需要删除的学生的编号!\n");
		return;
	}
	PNODE q = p->next;
	p->next = q->next;
	free(q);
	q == NULL;
	printf("你已经成功删除了第%d个学生的信息!\n",pos);
}

回到目录

创建单链表

链表的形成,有两种策略:有点栈和队列的意味。
前插法创建单链表: 后插入的值在链表的前面。
后插法创建单链表: 后插入的就挂在链尾即可。

  1. 采用后插法
void InputStudent(PNODE pHead)			//采用后插法创建单链表 
{
	NODE stu;
	PNODE p=pHead;
	PNODE pTail=NULL;
	while(NULL != p->next)
	{
		p = p->next;
	}
	pTail=p; 

	printf("请输入学生的人数:");
	scanf("%d",&len);
	int i;
	for(i=0; i<len; i++)
	{
		system("cls");				//清屏
		//下面得到数据域
		printf("请输入第%d个学生的姓名:\n", i+1);
		scanf("%s", stu.st.Name);
		printf("请输入第%d个学生的学号:\n", i+1);
		scanf("%d", &stu.st.No);
		printf("请输入第%d个学生的语文成绩:\n", i+1);
		scanf("%f", &stu.st.Score[0]);
		printf("请输入第%d个学生的数学成绩:\n", i+1);
		scanf("%f", &stu.st.Score[1]);
		printf("请输入第%d个学生的英语成绩:\n", i+1);
		scanf("%f", &stu.st.Score[2]);

		stu.st.Total = stu.st.Score[0] + stu.st.Score[1] + stu.st.Score[2];	//计算总分
		stu.st.Ave = stu.st.Total / 3.0f;									//计算平均分

		PNODE pNew = (PNODE)malloc(sizeof(NODE));							//为新节点分配内存
		if(NULL == pNew)															//判断内存是否为空
		{
			printf("内存分配失败,程序终止!\n");
			exit(-1);
		}

		pNew->st = stu.st;													//初始化结点的数据域	
		pNew->next = NULL;													//清空新结点的指针域
		//以上两步构建了新的尾巴 
		
		pTail->next = pNew;													//将新结点挂在了链上 
		//后插法,插入的位置就是即将成为尾部的位置 
		pTail = pNew;																//将pTail移到新的尾结点上
	}
}
  1. 采用前插法
PNODE InputStudent()									//采用前插法创建单链表 
{
	int len;//学生的人数
	NODE stu;
	PNODE pHead = (PNODE)malloc(sizeof(NODE));		//定义一个头结点并且为头结点分配内存
	if(NULL == pHead)	//判断内存是否为空
	{
		printf("内存分配失败,程序终止!\n");
		exit(-1);
	}
	pHead.next = NULL;//清空指针域
	printf("请输入学生的人数:");
	scanf("%d",&len);
	for(int i=0; i<len; i++)
	{
		system("cls");		//清屏
		//下面得到数据域
		printf("请输入第%d个学生的姓名:\n", i+1);
		scanf("%s", stu.st.Name);
		printf("请输入第%d个学生的年龄:\n", i+1);
		scanf("%d", &stu.st.Age);
		printf("请输入第%d个学生的学号:\n", i+1);
		scanf("%d", &stu.st.No);
		printf("请输入第%d个学生的语文成绩:\n", i+1);
		scanf("%f", &stu.st.Score[0]);
		printf("请输入第%d个学生的数学成绩:\n", i+1);
		scanf("%f", &stu.st.Score[1]);
		printf("请输入第%d个学生的英语成绩:\n", i+1);
		scanf("%f", &stu.st.Score[2]);

		stu.st.Total = stu.st.Score[0] + stu.st.Score[1] + stu.st.Score[2];//计算总分
		stu.st.Ave = stu.st.Total / 3.0f;			//计算平均分

		PNODE pNew = (PNODE)malloc(sizeof(NODE));	//为新节点分配内存
		if(NULL == pNew)							//判断内存是否为空
		{
			printf("内存分配失败,程序终止!\n");
			exit(-1);
		}

		pNew.st = stu.st;	//初始化结点的数据域	
		pNew.next = pHead.next;	//新结点的指针域为头结点的指针域 。
//		前插法插入的位置就是即将取代当前头结点的位置。
//		因为 pNew与pHead数据域不一样,所以不能 pNew=pHead。 
		pHead.next=pNew;	//更新头结点
	}
	return pHead;
}

回到目录

总的代码如下:

代码中将初始化嵌套在了输入函数中,也可以拿出开成为独立的功能函数。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Student			//学生结构
{
	char Name[10];		//姓名
	int No;				//学号
	float Score[3];		//三科的成绩
	float Total;		//总分
	float Ave;			//平均分
};

typedef struct Node		//结点
{
	struct Student st;	//数据域
	struct Node *next;	//指针域
}NODE, *PNODE;			//NODE等价于struct Student st||PNODE等价于struct Node *next
						//NODE * P==PNODE P。为了提高可读性用NODE定义指向其他任意节点。用PNODE专指向头结点 

PNODE InputStudent2();	//输入函数,用于输入学生信息(前插法) 
PNODE InputStudent(PNODE pHead);	//输入函数,用于输入学生信息(后插法) 
void OutputStudent(PNODE pHead);	//输出学生信息
void DeleteStudent(PNODE pHead);	//删除学生信息
void SearchStudent(PNODE pHead);	//查找学生信息
void ChangeStudent(PNODE pHead);	//修改学生信息
void InsertStudent(PNODE pHead);	//增加学生信息
void ScortByChinese(PNODE pHead);	//对学生的语文成绩排序
void ScortByMath(PNODE pHead);		//对学生的数学成绩排序
void ScortByEnglish(PNODE pHead);	//对学生的英语成绩排序
void ScortByTotal(PNODE pHead);		//对学生的总分排序

void welcome(){
	printf("========================================================\n\n");
	printf("========================================================\n\n");
	printf("**************欢迎使用学生成绩管理系统******************\n\n");
	printf("--------------------------------------------------------\n\n");
	printf("********************************************************\n\n");
	printf("========================================================\n\n");
	printf("请按任意将进入学生管理系统:\n");
	getchar();
} 

void menu(){
	printf("====================================================\n\n");
	printf("------------- 请选择要操作的命令:------------------\n\n");
	printf("------ 1  输入学生信息-------  2  输出学生信息------\n\n");
	printf("------ 3  删除学生信息-------  4  查找学生信息------\n\n");
	printf("------ 5  修改学生信息-------  6  增加学生信息------\n\n");
	printf("------ 7  将学生的语文成绩按从大到小排--------------\n\n");
	printf("------ 8  将学生的数学成绩按从大到小排--------------\n\n");
	printf("------ 9  将学生的英语成绩按从大到小排--------------\n\n");
    printf("------ 10 将学生的总成绩按从大到小排----------------\n\n");
	printf("====================================================\n\n");
	printf("------ ¤¤¤¤   其他键退出系统    ¤¤¤¤   ------\n\n");
	printf("====================================================\n\n");
}

int main()
{
	welcome();	
	system("cls");
	int Item;
	PNODE pHead = NULL;//定义一个指针
	while(1)
	{
		menu();
		printf("请选择操作命令:");
		scanf("%d",&Item);
		system("cls");
		switch(Item)
		{
		    case 1:pHead = InputStudent(pHead);break;
			case 2:OutputStudent(pHead);break;
			case 3:DeleteStudent(pHead);break;
			case 4:SearchStudent(pHead);break;
			case 5:ChangeStudent(pHead);break;
			case 6:InsertStudent(pHead);break;
			case 7:ScortByChinese(pHead);
				   OutputStudent(pHead);break;
			case 8:ScortByMath(pHead);
				   OutputStudent(pHead);break;
			case 9:ScortByEnglish(pHead);
				   OutputStudent(pHead);break;
			case 10:ScortByTotal(pHead);
				    OutputStudent(pHead);break;
			default:printf("系统已经退出,欢迎再次使用!\n"),exit(1); 
		}
	}
	system("pause");
}

PNODE InputStudent(PNODE pHead)			//输入函数,用于输入学生信息。采用后插法创建单链表 
{
	int len;							//学生的人数
	NODE stu;
	
	PNODE p=pHead;
	PNODE pTail=NULL;
	if(NULL == p)						//判断之前是否录入过数据 
	{
		p = (PNODE)malloc(sizeof(NODE));//为头结点分配内存
		if(NULL == p)					//判断内存是否为空
		{
			printf("内存分配失败,程序终止!\n");
			exit(-1);
		}
		pTail = p;						//尾指针初始时指向头结点
		pTail->next = NULL;				//清空指针域
	}
	else{
		PNODE temp = p;	
		while(NULL != temp->next)
		{
			temp = temp->next;
		}
		pTail=temp; 
	}

	printf("请输入学生的人数:");
	scanf("%d",&len);
	int i;
	for(i=0; i<len; i++)
	{
		system("cls");//清屏
		printf("请输入第%d个学生的姓名:\n", i+1);
		scanf("%s", stu.st.Name);
		printf("请输入第%d个学生的学号:\n", i+1);
		scanf("%d", &stu.st.No);
		printf("请输入第%d个学生的语文成绩:\n", i+1);
		scanf("%f", &stu.st.Score[0]);
		printf("请输入第%d个学生的数学成绩:\n", i+1);
		scanf("%f", &stu.st.Score[1]);
		printf("请输入第%d个学生的英语成绩:\n", i+1);
		scanf("%f", &stu.st.Score[2]);

		stu.st.Total = stu.st.Score[0] + stu.st.Score[1] + stu.st.Score[2];//计算总分
		stu.st.Ave = stu.st.Total / 3.0f;		//计算平均分

		PNODE pNew = (PNODE)malloc(sizeof(NODE));	//为新节点分配内存
		if(NULL == pNew)	//判断内存是否为空
		{
			printf("内存分配失败,程序终止!\n");
			exit(-1);
		}

		pNew->st = stu.st;	//初始化结点的数据域	
		pNew->next = NULL;	//清空新结点的指针域
		//以上两步构建了新的尾巴 
		
		pTail->next = pNew;	//将新结点挂在了链上 
		//后插法,插入的位置就是即将成为尾部的位置 
		pTail = pNew;	//将pTail移到新的尾结点上
	}
	return p;
}

PNODE InputStudent2()//输入函数,用于输入学生信息。采用前插法创建单链表 
{
	int len;//学生的人数
	NODE stu;
	PNODE pHead = (PNODE)malloc(sizeof(NODE));//定义一个头结点并且为头结点分配内存
	if(NULL == pHead)	//判断内存是否为空
	{
		printf("内存分配失败,程序终止!\n");
		exit(-1);
	}
	pHead->next = NULL;//清空指针域
	printf("请输入学生的人数:");
	scanf("%d",&len);
	int i;
	for(i=0; i<len; i++)
	{
		system("cls");//清屏
		printf("请输入第%d个学生的姓名:\n", i+1);
		scanf("%s", stu.st.Name);
		printf("请输入第%d个学生的学号:\n", i+1);
		scanf("%d", &stu.st.No);
		printf("请输入第%d个学生的语文成绩:\n", i+1);
		scanf("%f", &stu.st.Score[0]);
		printf("请输入第%d个学生的数学成绩:\n", i+1);
		scanf("%f", &stu.st.Score[1]);
		printf("请输入第%d个学生的英语成绩:\n", i+1);
		scanf("%f", &stu.st.Score[2]);

		stu.st.Total = stu.st.Score[0] + stu.st.Score[1] + stu.st.Score[2];//计算总分
		stu.st.Ave = stu.st.Total / 3.0f;		//计算平均分

		PNODE pNew = (PNODE)malloc(sizeof(NODE));	//为新节点分配内存
		if(NULL == pNew)	//判断内存是否为空
		{
			printf("内存分配失败,程序终止!\n");
			exit(-1);
		}

		pNew->st = stu.st;	//初始化结点的数据域	
		pNew->next = pHead->next;	//新结点的指针域为头结点的指针域 。
//		前插法插入的位置就是即将取代当前头结点的位置。
//		因为 pNew与pHead数据域不一样,所以不能 pNew=pHead。 
		pHead->next=pNew;	//更新头结点
	}
	return pHead;
}

void InsertStudent(PNODE pHead)//增加学生信息
{
	PNODE p = pHead;
	int i = 0;
	struct Student stu;//学生结构
	int pos;//插入结点的位置
	printf("请输入插入学生的位置:");
	scanf("%d",&pos);
	while(p && i<pos-1)
	{
		p = p->next;
		i++;
	}
	if(p == NULL || i>pos)
	{
		printf("插入结点的位置不存在!\n");
		return;
	}
	printf("你将在第%d个学生后面插入一个学生\n",pos-1);
	printf("请输入第%d个学生的姓名:",pos);
	scanf("%s",stu.Name);
	printf("请输入第%d个学生的学号:",pos);
	scanf("%d",&stu.No);
	printf("请输入第%d个学生的语文成绩:",pos);
	scanf("%f",&stu.Score[0]);
	printf("请输入第%d个学生的数学成绩:",pos);
	scanf("%f",&stu.Score[1]);
	printf("请输入第%d个学生的英语成绩:",pos);
	scanf("%f",&stu.Score[2]);

	stu.Total = stu.Score[0] + stu.Score[1] + stu.Score[2];//计算总分	
	stu.Ave = stu.Total / 3.0f;//计算平均分
	PNODE pNew = (PNODE)malloc(sizeof(NODE));
	if(NULL == pNew)
	{
		printf("动态内存分配失败,程序终止!\n");
		exit(-1);
	}
	pNew->st = stu;
	PNODE q = p->next;	//先把后继存起来 
	p->next = pNew;		//然后把要插的插进来 
	pNew->next = q;		//将刚刚保存的节点挂上 
}

void OutputStudent(PNODE pHead)//输出学生信息
{
	PNODE p = pHead->next;//定义一个指针用于遍历学生信息,p指向第一个节点 
	printf("   姓名    学号    语文    数学    英语     总分    平均分\n");	
	while(p)
	{
		printf("%6s  %8d  %6g  %6g  %6g  %6g  %10g\n", p->st.Name,p->st.No, p->st.Score[0], p->st.Score[1], p->st.Score[2], p->st.Total, p->st.Ave);
		p = p->next;
	}
}

void DeleteStudent(PNODE pHead)//删除学生信息
{
	PNODE p = pHead;
	int i = 0;
	int pos;
	printf("请输入你需要删除的学生的编号:");
	scanf("%d",&pos);
	while(p && i<pos-1)
	{
		p = p->next;
		i++;
	}

	if(p == NULL || i>pos-1)
	{
		printf("没找到需要删除的学生的编号!\n");
		return;
	}
	PNODE q = p->next;
	p->next = q->next;
	free(q);
	q == NULL;
	printf("你已经成功删除了第%d个学生的信息!\n",pos);
}

void SearchStudent(PNODE pHead)//查找学生信息
{
	char Name[10];
	printf("请输入你需要查找的学生的姓名:");
	scanf("%s",Name);
	PNODE p = pHead->next;//p指向第一个节点 
	while(p)
	{
		if(0 == strcmp(Name,p->st.Name))
		{
			printf("%s %d %g %g %g %g %g\n",p->st.Name, p->st.No, p->st.Score[0], p->st.Score[1], p->st.Score[2], p->st.Total, p->st.Ave);
		}
		p = p->next;
	}
}

void ChangeStudent(PNODE pHead)//修改学生信息
{
	char Name[10];
	printf("请输入你需要修改的学生的姓名:");
	scanf("%s",&Name);
	PNODE p = pHead->next;//定义一个指针用于遍历学生信息
	while(p)
	{
		if(0 == strcmp(Name, p->st.Name))
		{	
			printf("姓名 年龄  学号  语文  数学  英语  总分 平均分\n");			
			printf("修改前的学生信息!\n");
			printf("%s %d %g %g %g %g %g\n",p->st.Name, p->st.No, p->st.Score[0], p->st.Score[1], p->st.Score[2], p->st.Total, p->st.Ave);
			system("pause");
			system("cls");//清屏
			printf("请输入新的学生姓名:");
			scanf("%s", p->st.Name);
			printf("请输入新的学生学号:");
			scanf("%d", &p->st.No);
			printf("请输入新的学生的语文成绩:");
			scanf("%f", &p->st.Score[0]);
			printf("请输入新的学生的数学成绩:");
			scanf("%f", &p->st.Score[1]);		
			printf("请输入新的学生的英语成绩:");
			scanf("%f", &p->st.Score[2]);
						
			p->st.Total = p->st.Score[0] + p->st.Score[1] + p->st.Score[2];//计算总分		
			p->st.Ave = p->st.Total / 3.0f;	//计算平均分
			break;
		}
	  p = p->next;
	}
}

void ScortByChinese(PNODE pHead)//对学生的语文成绩排序
{
	PNODE p, q;//定义两个指针	
	NODE temp;
	for(p=pHead->next; NULL != p; p=p->next)
	{
		for(q=p->next; NULL !=q; q=q->next)
		{
			if(p->st.Score[0] < q->st.Score[0])//当前一个学生的语文成绩小于后一个学生的语文成绩时
			{
				temp.st  = p->st;//交换学生的位置
				p->st =  q->st;
				q->st = temp.st;
			}
		}
	}
}

void ScortByMath(PNODE pHead)//对学生的数学成绩排序
{
	PNODE p, q;//定义两个指针	
	NODE temp;
	for(p=pHead->next; NULL != p; p=p->next)
	{
		for(q=p->next; NULL !=q; q=q->next)
		{
			if(p->st.Score[1] < q->st.Score[1])//当前一个学生的数学成绩小于后一个学生的数学成绩时
			{
				temp.st  = p->st;//交换学生的位置
				p->st =  q->st;
				q->st = temp.st;
			}
		}
	}
}

void ScortByEnglish(PNODE pHead)//对学生的英语成绩排序
{
	PNODE p, q;//定义两个指针	
	NODE temp;
	for(p=pHead->next; NULL != p; p=p->next)
	{
		for(q=p->next; NULL !=q; q=q->next)
		{
			if(p->st.Score[2] < q->st.Score[2])//当前一个学生的英语成绩小于后一个学生的英语成绩时
			{
				temp.st  = p->st;//交换学生的位置
				p->st =  q->st;
				q->st = temp.st;
			}
		}
	}
}

void ScortByTotal(PNODE pHead)//对学生的总分排序
{
	PNODE p, q;//定义两个指针	
	NODE temp;
	for(p=pHead->next; NULL != p; p=p->next)
	{
		for(q=p->next; NULL !=q; q=q->next)
		{
			if(p->st.Total < q->st.Total)//当前一个学生的总分小于后一个学生的总分时
			{
				temp.st  = p->st;//交换学生的位置
				p->st =  q->st;
				q->st = temp.st;
			}
		}
	}
}

运行结果:

后插法得到的如下:
在这里插入图片描述
前插法得到的如下:
在这里插入图片描述
从上面可以看出前插法与后插法得到的顺序相反。

回到目录

顺序表与链表的比较

在这里插入图片描述
回到目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值