关于链表的程序,请高手指教
#include<iostream>#include<fstream>
#include<iomanip>
#include<conio.h>
using namespace std;
struct student
{
char name[20];
char number[20];
int year;
int month;
char hobby[20];
//hobby[20]代表爱好
student *next;//下一个学生结点指针
};
int num=0;
//s表示一组学生信息链表的表头指针
void input_student(student *&s);
//采用插入法,边插入边按由大到小排序
//形参*&s表示s是一个引用指针,用于返回输入的指针值
void output_student(student *p);
//输出p为头指针的链表表示的学生信息。
void save_student(char *n,student *p);
//将以p为头指针的链表表示的学生信息保存在以字符串n为文件名的磁盘文件中
void read_student(char *n,student *&p);
void delete_student(student *&s);
//在以s为链表头结点指针的链表中,删除一个结点,s作为引用指针变量
//是为了返回链表头结点指针
int main()
{
student *s=NULL;//s为链表头指针
cout<<"请输入一组学生数据"<<endl;
input_student(s);
cout<<"学生信息为"<<endl;
output_student(s);
cout<<"将学生信息保存在磁盘文件中,输入磁盘文件名"<<endl;
char cdname[20];
//cdname[20]代表磁盘名字
cin>>cdname;
save_student(cdname,s);
cout<<"从磁盘中读入一组学生数据,请输入磁盘文件名"<<endl;
cin>>cdname;
read_student(cdname,s);//从磁盘读入学生数据
cout<<"读入的学生信息"<<endl;
output_student(s);
//删除学生信息
delete_student(s);
cout<<"删除一个结点后的链表信息为"<<endl;
output_student(s);
cout<<"将学生信息再保存回磁盘文件中,请输入磁盘文件名"<<endl;
cin>>cdname;
save_student(cdname,s);
system("pause");
return 0;
}
void input_student(student *&s)
{
student *t;//流动结点指针,用于指向新产生的结点
cout<<"请输入学生信息"<<endl;
do
{
char name[20]={'0'};
//name[20]代表姓名
char number[20]={'0'};
//number[20]代表学号
int year=0,month=0;
char hobby[20]={'0'};
//hobby[20]代表爱好
cout<<"学生姓名(*为终止标志):";cin>>name;
if(strcmp(name,"*")==0)break;
m:cout<<"请输入学号"<<endl;
for(int i=0;i<9;i++)
{
number[i]=getch();
putch(number[i]);
//这样设置最多只能输入9位
if(!isdigit(number[i])&&number[i]!=13)
//isdigit判断一个数是否整数。
//13表示回车
{
cout<<int(number)<<endl;
cout<<"你敲的不是数字, 请重新输入!\n";
goto m;
}
}
cout<<endl;
char year1[15];
//用于存出生年份的字符数组
w1:cout<<"学生出生年份:";
for(int i=0;i<4;i++)
{
year1[i]=getch();
putch(year1[i]);
//这样设置最多只能输入4位
if(!isdigit(year1[i])&&year1[i]!=13)
//isdigit判断一个数是否整数。
//13表示回车
{
cout<<int(year1)<<endl;
cout<<"你敲的不是数字, 请重新输入!\n";
goto w1;
}
}
year=atol(year1);
cout<<endl;
char month1[10];
//用于存月份的字符数组
w2:cout<<"学生出生月份:";
for(int i=0;i<2;i++)
{
month1[i]=getch();
putch(month1[i]);
//这样设置最多只能输入2位
if(!isdigit(month1[i])&&month1[i]!=13)
//isdigit判断一个数是否整数。
//13表示回车
{
cout<<int(month1)<<endl;
cout<<"你敲的不是数字, 请重新输入!\n";
goto w2;
}
}
month=atol(month1);
if(month>12)
{
cout<<"你输错了,请重新输入"<<endl;
goto w2;
}
cout<<endl;
cout<<"爱好:";cin>>hobby;
num++;
t=new student;
//t->name=name;
strcpy(t->name,name);
strcpy(t->number,number);
t->year=year;
t->month=month;
strcpy(t->hobby,hobby);
t->next=NULL;
//给新结点填充数据,下一项指针next置空
if(!s)//原头指针空
s=t; //新结点指针作为头指针
else
{
student *p=s;//p作为循环用的流动指针,从头指针开始
if(t->year>p->year||(t->year==p->year&&t->month>=p->month))
//t指向的结点比原来头指针指向的结点大
{
s=t;//t取代头指针s,原头指针p作为新头指针s的后继
s->next=p;
}
else
{
student *q;//设置指针q用于查找新指针t在链表中位置的前驱
while(p&&(t->year<p->year)||(t->year==p->year&&t->month<p->month))
//查找新结点应该插入的位置q之后p之前
{
q=p;
p=p->next;
}// while循环终止时,有两种情况,1,t在q和p之间直接插入
//2,t在结尾之后q指向尾结点,p空指针,将t接在q后,t接空指针
q->next=t;
t->next=p;
}
}
}while(1);//while条件永真,出口设在输入姓名“*”处
}
void output_student(student *p)
{
student *t=p;//t作为流动指针
cout<<"共有"<<num<<"个学生,从小到大排列为"<<endl;
cout<<"姓名"<<setw(10)<<"学号"<<setw(15)<<"出生年份"
<<setw(10)<<"出生月份"<<setw(10)<<"爱好"<<endl;
while(t)//t不空,表示t所指向的结点存在,输出相关信息
{
cout<<t->name<<setw(15)<<t->number<<setw(10)<<t->year
<<setw(10)<<t->month<<setw(13)<<t->hobby<<endl;
t=t->next;
}
}
void save_student(char *n,student *p)
{
ofstream out(n,ios::out|ios::binary);
if(!out)
{
cout<<"磁盘文件"<<n<<"打开失败"<<endl;
system("pause");
return;
}
out.write((char*)&num,sizeof(int));
//保存学生人数,以便读文件时使用
for(student *t=p;t;t=t->next)
{
//t作为流动指针,从头指针p开始,当前结点输出之后,指针后移一项
out.write((char*)t,sizeof(student));
//第一个参数是输出数据的位置,即指针t所指结点的地址
//即&(*t),实际上就是t
//第二个参数是输出数据的长度,即输出一个结点的长度
}
out.close();
cout<<"输出结点的个数为"<<num<<endl;
}
void read_student(char *n,student *&p)
{
ifstream in(n,ios::in|ios::binary);
if(!in)
{
cout<<"磁盘文件"<<n<<"打开失败"<<endl;
system("pause");
return;
}
p=NULL;
if(!in.eof())
in.read((char*)&num,sizeof(int));
if(num>0)
{
p=new student;
in.read((char*)p,sizeof(student));
//第一个参数是磁盘数据读入内存的位置,即指针p所指结点的地址即&(*p)实际上就是p
//第二个参数是读入数据的长度,即一个结点的长度
}
p->next=NULL;
student *q=p;
//q,t是为了从文件中读出结点数据后,形成链表用的,q为已存在的结点指针
//t为新形成的结点指针,t为q的后继,同时,再次生成t之前,q后移一项
student *t;
for(int i=1;i<num;i++)
{
t=new student;
in.read((char*)t,sizeof(student));
t->next=NULL;
//因为新链表只需要磁盘文件中的实际数据,读入结点的后继结点指针不起作用
//新链表的结点地址要靠new student来分配,在下一个结点读入之前,后继
//指针置空
q->next=t;
q=t;
}
//此处使用for循环,而不使用while循环,是因为若使用成员函数eof()来控制循环结束
// 文件的eof()函数有一个问题,文件读完之后,他仍不为真,必须试图读取文件结束标志之后
//eof()才为真,这样以来,若使用while(!in.eof())直接控制读完整个文件,势必多读出一个结点
//为此,在文件开头保存结点个数,再使用for循环控制,读出全部结点
in.close();
}
void delete_student(student *&s)
{
char name[20];
cout<<"请输入要删除学生名单:";cin>>name;
student *t=s;
if(strcmp(t->name,name)==0)
{
s=t->next;
delete t;
num--;
return;
}
//要删除学生结点为链表头,上述程序删除头结点
//下面在链表中先查找要删除的结点,再删除
student *q=t;//t作为流动指针,q作为t的前驱结点指针
while(t&&strcmp(t->name,name))
{
q=t;
t=t->next;
}
if(t)
{
q->next=t->next;
delete t;
num--;
}
else
{
cout<<"没有找到要删除的学生"<<endl;
system("pause");
exit(1);
}
}