/*********************************
Project: 学生信息管理系统
Writer: SHOW
Time: 2011/12/25
Function: 通过链表可以任意添加学生信息,查找学生信息,删除学生信息,显示所有学生信息,按各科成绩对学生进行排序。
涉及知识点:1.单链表的插入,删除,查找
2.二级指针,及初始化问题
版本:2.0
1. 结构中新加了语文,数学,英语成绩。并且可以按三门成绩对学生信息进行排序,也就是Sort_Node对节点进行排序
2. 解决了版本1.0留下的问题 1。原因是因为在主函数里没有把*phead 根指针赋予NULL。而是(*phead) -> next =NULL,
这样程序一开始就插入了头结点。
*********************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<conio.h>
/* 定义一个学生信息的结构 */
typedef struct address{
char name[8];
char tel[20];
int chinese;
int math;
int english;
struct address *next;
int num;
}ADDR;
//========================================================================
// 函数说明: 单链表插入节点函数
// 实现功能: 能在任意位置插入节点,参考C和指针的经典写法
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针。
// new_num: 为插入的序号,提供有序插入的信息。
// 返回值: 无
//========================================================================
void Insert_Node(ADDR **phead,int new_num,char name[],char tel[],int chinese,int math,int english)
{
ADDR *current;
ADDR *newnode;
while( (current = *phead) != NULL && current -> num <= new_num)
phead = ¤t -> next;
newnode = (ADDR*)malloc(sizeof(ADDR));
/* 插入的学生信息 */
strcpy(newnode -> name,name);
strcpy(newnode -> tel,tel);
newnode -> num = new_num;
newnode -> chinese = chinese;
newnode -> math = math;
newnode -> english = english;
/*******************/
newnode -> next = current;
*phead = newnode;
}
//========================================================================
// 函数说明: 单链表查找节点函数,
// 实现功能: 通过传入的序号找到该节点并返回节点指针
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针。
// seek_num: 需要查找的序号
// 返回值: 如果找到了节点,则返回该节点,遍历后没有找到返回NULL
//========================================================================
ADDR* Seek_Node(ADDR **phead,int seek_num)
{
ADDR *current;
while( (current = *phead) != NULL && current -> num != seek_num)
{
phead = ¤t -> next;
}
if(current == NULL)
return NULL;
if(current -> num == seek_num)
return current;
else
return NULL;
}
//========================================================================
// 实现功能: 单链表删除节点函数,通过传入的数据域seek_num序号,删除对应节点。
// 函数说明: 此写法参考尚观C视频教程写法,删除后必须把该节点的前一个节点指向后一节点,要不然链表就乱了。
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针。
// new_num: 为删除的序号
// 返回值: 删除成功返回头结点*phead,反之返回NULL。
//========================================================================
ADDR* Delete_Node(ADDR **phead,int seek_num)
{
ADDR *p,*q;
if(*phead == NULL)
return NULL;
else if((*phead) -> num == seek_num)
{
p= *phead;
*phead = (*phead) -> next;
}
else
{
p = *phead;
while((p -> num != seek_num) && (p -> next != NULL))
{
q = p;
p = p-> next;
}
if(p -> num != seek_num)
return NULL;
else
{
q -> next = p -> next;
free(p);
}
}
return *phead;
}
//========================================================================
// 函数说明: 单链表获取节点长度函数
// 实现功能: 通过遍历整个链表只要没到尾部 lengh++。
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针。
// 返回值: 返回节点个数
//========================================================================
int Get_NodeLengh(ADDR **phead)
{
ADDR *current;
int lengh = 0;
while( (current = *phead) != NULL)
{
lengh ++;
phead = ¤t -> next;
}
return lengh;
}
//========================================================================
// 函数说明: 单链表数据域排序函数
// 实现功能: 运用冒泡排序算法进行排序,但只是移动了数据的位置,而没有对节点进行移动。
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针。
// score:为需要进行排序的数据域值
// 返回值: 返回根指针
//========================================================================
/*ADDR* Sort_Node_Data(ADDR **phead,int score)
{
ADDR *p;
int n,m,lengh,temp;
lengh = Get_NodeLengh(phead);
p = *phead;
for( n=0; n<=lengh; n++)
{
p = *phead;
for(m=0; m<(lengh - n); m++)
{
if(p->score > p->next->score)
{
temp = p -> score;
p -> score = p -> next -> score;
p -> next -> score = temp;
}
p = p -> next;
}
}
return *phead;
}*/
//========================================================================
// 函数说明: 单链表节点排序函数
// 实现功能: 对节点进行排序,移动节点的位置
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针
// 返回值: 返回根指针
//========================================================================
ADDR* Sort_Node(ADDR **phead,char flag)
{
ADDR *p,*q,*t,*s,*h;
h=(ADDR *)malloc(sizeof(ADDR));
h->next=*phead;
p=h;
if((*phead) -> next == NULL)
return NULL;
while(p->next->next!=NULL)
{
for(s=p,q=p->next;q->next!=NULL;q=q->next)
{
switch(flag)
{
case 'n':
if(q->next->num < s->next->num)
s = q;
break;
case 'c':
if(q->next->chinese<s->next->chinese)
s=q;
break;
case 'm':
if(q->next->math<s->next->math)
s=q;
break;
case 'e':
if(q->next->english<s->next->english)
s=q;
break;
default: break;
}
}
if(s!=q)
{
t=s->next;
s->next=t->next;
t->next=p->next;
p->next=t;
}
p=p->next;
}
*phead=h->next;
free(h);
return *phead;
}
/* 函数声明 */
void InputStudent(ADDR **phead);
void DisplayAllStudent(ADDR **phead);
void SeekStudent(ADDR **phead);
void MenuSelect(void);
void HandleMenu(ADDR **phead);
void DeleteStudent(ADDR **phead);
void SortStudent(ADDR **phead);
/* 用带参数的宏分配内存 */
#define ASK(top) do{\
top=(ADDR *)malloc(sizeof(ADDR));\
if(top==NULL){printf("memory fail!");}\
}while(0);
/* 主函数 */
int main(void)
{
ADDR **phead; // 定义一个二级指针
ADDR *Temp;
ASK(Temp);
phead = &Temp; // 必须先给指针初始化内存,不然程序果断崩溃 ,也是自己用指针经常犯的错误谨记 ¥¥¥¥¥
memset(*phead,NULL,sizeof(ADDR));
(*phead) = NULL;
while(1)
{
HandleMenu(phead);
}
return 1;
}
//========================================================================
// 函数说明: 添加学生信息子函数
// 实现功能: 添加一个学生信息,添加一个节点。学生信息通过scanf函数从控制台输入
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针。
// 返回值: 无
//========================================================================
void InputStudent(ADDR **phead)
{
int num;
char name[8];
char tel[20];
int chinese,math,english;
printf(" --- 请输入新加入学生信息: ---\n");
while(1)
{
printf("学号:");
scanf("%d",&num);
printf("姓名:");
scanf("%s",name);
printf("电话号码:");
scanf("%s",tel);
printf("语文:");
scanf("%d",&chinese);
printf("数学:");
scanf("%d",&math);
printf("英语:");
scanf("%d",&english);
Insert_Node(phead,num,name,tel,chinese,math,english);
printf("任意键继续添加信息,输入0 返回\n");
if(getch() == '0')
break;
else
continue;
}
}
//========================================================================
// 函数说明: 显示所有学生信息子函数
// 实现功能: 遍历整个链表,把所有链表信息都打印出来
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针
// 返回值: 无
//========================================================================
void DisplayAllStudent(ADDR **phead)
{
ADDR *current;
/* 只要不为NULL,也就是没到尾部。那么找到的节点则都为先前插入的节点,也就是肯定存在信息的*/
while( (current = *phead) != NULL )
{
printf("学号:%d,姓名:%s,电话号码:%s,语文:%d,数学:%d,英语:%d\n",
current -> num,current -> name,current -> tel,current -> chinese, current -> math, current -> english);
phead = ¤t -> next;
}
}
//========================================================================
// 函数说明 查找学生信息子函数
// 实现功能: 通过键盘输入命令,判断查找单个学生还是所有学生信息。
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针
// 返回值: 无
//========================================================================
void SeekStudent(ADDR **phead)
{
int num;
char name[20];
char tel[100];
ADDR *temp_node;
printf("--- 任意键查找单个学生信息,查找所有学生信息请输入 a ---\n");
if(getch() == 'a')
{
DisplayAllStudent(phead);
printf("返回请输入 0\n");
/* 这里用while也是因为返回有清屏操作,为了让控制台显示所有同学信息,所以要先停在这里。*/
while(1)
{
if(getch() == '0')
break;
}
}
else
{
while(1)
{
printf("学号:");
scanf("%d",&num);
temp_node = Seek_Node(phead,num);
if(temp_node == NULL)
{
printf("输出错误不存在此学号, 请重新输入 ,输入 0 返回\n");
if(getch() == '0')
break;
else
continue;
}
else
{
printf("该学生信息为 -> 学号:%d,姓名:%s, 电话号码:%s,语文:%d,数学:%d,英语:%d\n",
temp_node -> num,temp_node -> name,temp_node -> tel,temp_node -> chinese,temp_node -> math,temp_node -> english );
printf("输入任意键继续查找,输入0 返回\n");
if(getch() == '0')
break;
else
continue;
}
}
}
}
//========================================================================
// 函数说明 学生信息排序子函数
// 实现功能:
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针
// 返回值: 无
//========================================================================
void SortStudent(ADDR **phead)
{
int score;
char ch;
printf("按学号,语文,数学,英语排序请分别输入n, c , m , e 返回请输入 0 \n");
while(1)
{
ch = getch();
if(ch == '0')
break;
else
{
switch(ch)
{
case 'n':
printf("按学号排序结果如下:\n");
break;
case 'c':
printf("按语文成绩排序结果如下:\n");
break;
case 'm':
printf("按数学成绩排序结果如下:\n");
break;
case 'e':
printf("按英语成绩排序结果如下:\n");
break;
default: break;
}
Sort_Node(phead,ch);
DisplayAllStudent(phead);
printf("\n");
}
}
}
//========================================================================
// 函数说明: 删除学生信息子函数
// 实现功能: 通过scanf从控制台输入需要删除的学号,再调用对应Delete_Node函数删除对应学号的节点
// 入口参数: phead:指向头结点的指针的指针,也就是指向根指针的指针
// 返回值: 无
//========================================================================
void DeleteStudent(ADDR **phead)
{
int num;
char ch;
printf("--- 删除单个学生信息请输入 s,删除所有学生信息请输入 a / A ---\n");
ch = getch();
if(ch == 's')
{
while(1)
{
printf("学号:");
scanf("%d",&num);
if(Delete_Node(phead,num) == NULL)
{
printf("输入错误,不存在此学号,请重新输入,输入 0 返回\n");
if(getch() == '0')
break;
else
continue;
}
else
{
printf("任意键删除下一个,输入 0 返回\n");
if(getch() == '0')
break;
else
continue;
}
}
}
else if(ch == 'a' || ch == 'A')
{
(*phead) = NULL; // 直接给根指针NULL
}
}
/* 菜单子函数 */
void MenuSelect(void)
{
printf("*******************************学生信息管理系统*********************************\n");
printf(" 添加学生信息请输入 1 删除学生信息请输入 2 \n");
printf(" 查找学生信息请输入 3 对成绩排序请输入 4 \n");
printf("********************************************************************************\n");
}
/* 处理菜单子函数 */
void HandleMenu(ADDR **phead)
{
char ch;
system("cls"); //调用系统命令清屏操作
MenuSelect();
ch = getch();
switch(ch)
{
case '1': InputStudent(phead);
break;
case '2': DeleteStudent(phead);
break;
case '3': SeekStudent(phead);
break;
case '4': SortStudent(phead);
break;
default : break;
}
}