#include<stdio.h>//本代码意在锻炼对链表这一数据类型的管理。用readDataFromFile函数和saveData函数读取和
#include<stdlib.h>//保存链表文件(注意:这里仅保存了链表的数据域),createList完成链表头节点的创建,inser
#include<string.h>//tdata函数和deletedata函数完成节点的插入和删除(插入有头插,尾插,任意插入,这里要
//注意任意插入时p->next=temp->next,temp->next=p完成插入,删除有头删,尾删和任意位
typedef struct Student{ //置删除,任意位置删除时用temp->next=p->next完成删除。有意思的是,插入与
char name[20]; //删除,均只与前一个节点有关),用finddata函数和changedata函数完成链表的
int num; //的查找和修改,基本思想是for循环遍历查找。sort函数完成链表的排序(包括
//冒泡,选择和脱链,这里最重要的是脱链,其中的i->data.num>temp->next->dat
//a.num秀我一脸,成功将temp和i的上一个节点对上,值得学习)
//本文要点1:回车残留 因要输入姓名字符串,要时刻注意回车残留。注意使用
//getchar().
//要点2:for遍历循环,开始时看要不要参与运算比较选择head还是head->next,
//结束时,temp!=NULL遍历完整个链表,temp->next!=NULL到倒数第二个节点,以
//此类推。
//要点3:结构体指针要么指向一个结构体整体,要么malloc指向堆区。千万千万注意
//指针的指向。
//本文使用了宏定义,typedef 和枚举,个人感觉可以将函数指针用上去,更简洁
//本文充分体现了DRY原则,能重复的都尽量用函数封装了。
}Student;//将数据域与地址域分开,便于运算。且为下文的文件保存读取准备了便利条件。
typedef struct LINK{
Student data;
struct LINK *next;
}LINK,*pLINK;
typedef enum IsRepeat//枚举 增强文件的可读性 在checkrepeatdata函数和getData函数中运用。
{
YES,
NO
}IsRepeat;
/*
enum season{
spring 1;
summer 3;
autumn;//不写的话,依次递增为4
winter;
}
*/
Student getData(pLINK head);//节点信息输入函数声明
int getNumber();//节点数输入函数声明
int getListLength(pLINK head)//链表长度函数,使用for循环遍历得到,有2种 head到temp->next!=NULL和head
{ //->next到temp!=NULL,个人认为后一种更易理解,都是数据节点。头节点没数据,
int j=0; //不算到链表长度里的。而保存到文件中的是数据域,头节点自然也没份。
pLINK temp;
for(temp=head;temp->next!=NULL;temp=temp->next)
{
j++;
}
return j;
}
IsRepeat checkRepeatData(pLINK head,Student stu)//查重函数 for循环遍历,if条件判断,若能跑完整个循环
{ //自然是没有重复的。
pLINK temp;
for(temp=head->next;temp!=NULL;temp=temp->next)
{
if(temp->data.num==stu.num)
{
return YES;
}
}
if(temp==NULL)
{
return NO;
}
return NO;
}
#define filename\//宏定义 一般便于理解和很多字符要打,为便于编写时使用。
"/home/lifulin/5"
void myScanf(char *str,int size)//字符串输入函数,很典型 size内时遇\n止,并改\n为\0;size-1之外(跑完
{ //整个for循环)后,直接让str[i]='\0'(size-1的用意体现出来了),因为只能
int i=0; //放这么多,之后的缓存都吸收释放掉。
for(i=0;i<size-1;i++)
{
str[i]=getchar();
if(str[i]=='\n')
{
break;
}
}
//说明在中途碰到了\n
if(str[i]=='\n')//注意插入时的回车残余,要用getchar()吸收。
{
str[i]='\0';
}
else
{
str[i]='\0';
while(getchar()!='\n');//while循环用getchar()吸收之后的输入缓存。
}
}
//创建链表函数封装
pLINK createList(pLINK head)
{
if(head==NULL)//防内存泄露。
{
head=(pLINK)malloc(sizeof(LINK));//if循环里可重定义,不好找出bug。
head->next=NULL; //代码块(花括号)作用域不一样。
}
printf("链表创建成功\n");
return head;
}
//链表打印函数封装
void printData(pLINK head)
{
if(head==NULL)//意思是有头节点,没有其他数据节点顶多不打印,不会报错。
{ //连头节点都没有,直接写temp=head->next,直接报错。
printf("无信息打印\n");
return;
}
pLINK temp=head->next;
printf("head->");
for(temp=head->next;temp!=NULL;temp=temp->next)//标准的for循环遍历
printf("[%s,%d]->",temp->data.name,temp->data.num);
printf("NULL\n");
}
//链表头插函数封装
void headInsertData(pLINK head)
{
/* if(head==NULL)//insertData函数中已考虑
{
printf("链表没有创建\n");
return;
}*/
pLINK p=(pLINK)malloc(sizeof(LINK));
p->next=head->next;//将p指向的节点和原head的后一个节点相连。
head->next=p;//将head指向的节点与p指向的节点相连。这样2边都连上,头插成功。
p->data=getData(head);//输入数据。
printf("数据头插成功\n");
}
//节点信息输入函数封装
Student getData(pLINK head)
{
printf("请输入学生信息[name num]:\n");
Student stu;
myScanf(stu.name,20);
scanf("%d",&stu.num);
getchar();
int errorCount=0;
while(checkRepeatData(head,stu)==YES)//枚举
{
errorCount++;
if(errorCount==3)
{
printf("输入错误次数过多\n");
exit(0);//整数即可,整个程序退出,强制退出。(错误码)
}
printf("该学号学生已存在\n");
printf("请重新输入数据[姓名 学号]:\n");
myScanf(stu.name,20);
scanf("%d",&stu.num);
getchar();
}
return stu;
}
void tailInsertData(pLINK head)//尾插函数封装
{
/* if(head==NULL)
{
printf("链表没有创建\n");
return;
}*/
//找尾指针:最后一个数据节点的地址
pLINK temp;
for(temp=head;temp->next!=NULL;temp=temp->next)
{
}//这个循环跳出的条件:temp->next==NULL
pLINK p=(pLINK)malloc(sizeof(LINK));
p->data=getData(head);//输入数据 很巧妙的一步。
temp->next=p;//这一步就完成了尾插的8成工作。
p->next=NULL;
printf("数据尾插成功\n");
}
void headDeleteData(pLINK head)
{
/* if(head==NULL)//在deleteData函数中已考虑
{
printf("链表没有创建\n");
return;
}*/
if(head->next==NULL)
{
printf("没有数据可删除\n");
return;
}
pLINK temp=head->next;
head->next=temp->next;//将head指向的节点的next指针直接指向head->next->next的节点,绕过了原head后的
free(temp); //的节点,完成头删。
printf("头删成功\n");
}
void tailDeleteData(pLINK head)
{
/* if(head==NULL)
{
printf("链表没有创建\n");
return;
}*/
if(head->next==NULL)
{
printf("没有数据可删除\n");
return;
}
pLINK temp=NULL;
pLINK temp1=NULL;
temp=head;
temp1=temp->next;
while(temp1->next!=NULL)//等待temp1->next==NULL;
{
temp=temp->next;
temp1=temp->next;
}
temp->next=NULL;
free(temp1);
temp1=NULL;//空间虽然释放了,但指针仍可指向该地址,故改NULL。
printf("尾删成功\n");
}
void randomDeleteData(pLINK head)
{
/*if(head==NULL)//在deleteData函数中已考虑。
{
printf("链表没有创建\n");
return;
}*/
if(head->next==NULL)
{
printf("没有数据可删除\n");
return;
}
int j=0;
pLINK p=head;
for(p=head;p->next!=NULL;p=p->next)
{
j++;
}
int select;
int i=0;
select=getNumber();
if(select>j||select<=0)
{
printf("超出查询范围\n");
return;
}
pLINK temp=head;
pLINK temp1=temp->next;
for(i=1;i<select;i++)
{
temp=temp->next;
temp1=temp1->next;
}
temp->next=temp1->next;
free(temp1);
printf("删除成功\n");
}
int getNumber()
{
int select;
printf("输入要处理的节点的节点数\n");
scanf("%d",&select);
getchar();//在任意位置插入节点时与myScanf连用,为了消除回车残余而如此使
return select; // 用。
}
void randomInsertData(pLINK head)
{
/* if(head==NULL)
{
printf("链表没有创建\n");
return;
}*/
/* if(head->next==NULL)
{
printf("没有数据可删除\n");
return;
}*/
int j=0;
pLINK p1=head;
for(p1=head;p1->next!=NULL;p1=p1->next)
{
j++;
}
int select=0;
int i=0;
select=getNumber();
printf("%d %d\n",j,select);
if(select>j+1||select<0)
{
printf("超出插入范围\n");
return;
}
/* int select;
select=getNumber();
int i;
*/ pLINK temp=head;
for(i=0;i<select-1;i++)
{
temp=temp->next;
}
pLINK p=(pLINK)malloc(sizeof(LINK));
p->next=temp->next;
temp->next=p;
p->data=getData(head);
}
void insertData(pLINK head)
{
if(head==NULL)
{
printf("链表没有创建\n");
return;
}
int select;
while(1)
{
printf("==============\n");
printf("1.头插节点\n");
printf("2.尾插节点\n");
printf("3.任意位置插入节点\n");
printf("4.返回上一层\n");
printf("==============\n");
scanf("%d",&select);
getchar();//清除缓存\n。//其余的不需要吸收回车残余,因为不是字符串
switch (select) //输入,会智能识别数字输入。数字输入遇
{ //空格和回车即停止。
case 1:
headInsertData(head);
break;
case 2:
tailInsertData(head);
break;
case 3:
randomInsertData(head);
break;
case 4:
return;
default:
printf("输入错误,请重新输入\n");
break;
}
}
}
void deleteData(pLINK head)
{
if(head==NULL)
{
printf("链表没有创建\n");
return;
}
int select;
while(1)
{
printf("==========\n");
printf("1.头删节点\n");
printf("2.尾删节点\n");
printf("3.任意位置删除节点\n");
printf("4.返回上一层\n");
printf("==========\n");
scanf("%d",&select);
getchar();
switch (select)
{
case 1:
headDeleteData(head);
break;
case 2:
tailDeleteData(head);
break;
case 3:
randomDeleteData(head);
break;
case 4:
return;
default:
printf("输入错误,请重新输入\n");
break;
}
}
}
void searchData(pLINK head)
{
if(head==NULL||head->next==NULL)
{
printf("无信息可查询\n");
return;
}
int num;
printf("请输入要查询的学号\n");
scanf("%d",&num);
pLINK temp;
for(temp=head->next;temp!=NULL;temp=temp->next)
{
if(temp->data.num==num)
{
printf("[%s %d]\n",temp->data.name,temp->data.num);
return;
}
}
if(temp==NULL)
{
printf("查无此人\n");
return;
}
}
void changeData(pLINK head)
{
if(head==NULL||head->next==NULL)
{
printf("无信息可修改\n");
return;
}
char name[20];
printf("请输入姓名\n");
myScanf(name,20);
pLINK temp;
for(temp=head->next;temp!=NULL;temp=temp->next)
{
if(strcmp(temp->data.name,name)==0)
{
printf("name=%s,number=%d\n",temp->data.name,temp->data.num);
printf("请输入修改的学生信息\n");
myScanf(temp->data.name,20);
scanf("%d",&temp->data.num);
printf("修改后的学生信息\n");
printf("name=%s,number=%d\n",temp->data.name,temp->data.num);
printf("修改成功\n");
break;
}
}
if(temp==NULL)
{
printf("查无此人\n");
return;
}
}
void saveData(pLINK head)//头节点因为没有数据,所以是读不进去的。
{
if(head==NULL||head->next==NULL)
{
printf("无信息可保存\n");
return;
}
FILE *fp=fopen("/home/lifulin/5","w");
if(fp==NULL)
{
printf("文件没打开\n");
return;
}
pLINK temp;
for(temp=head->next;temp!=NULL;temp=temp->next)
{
fwrite(&temp->data,sizeof(Student),1,fp);
}
fclose(fp);
printf("保存成功\n");
/* int j=0;
rewind(fp);
pLINK temp=head;
for(temp=head;temp->next!=NULL;temp=temp->next)
{
j++;
}
printf("j=%d\n",j);
fwrite(head,sizeof(LINK),j,fp);
fclose(fp);
printf("文件保存成功\n");*/
}
pLINK readDataFromFile()
{
/* pLINK head=(pLINK)malloc(sizeof(LINK));
head->next=NULL;
FILE *fp=fopen(filename,"r");
if(fp==NULL)
{
printf("open failed\n");
return NULL;
}//fread返回你成功读取的数据个数。
Student stu;
while(fread(&stu,sizeof(stu),1,fp)==1)
{
pLINK p=(pLINK)malloc(sizeof(LINK));
p->data=stu;
//尾插,找到最后一个节点
pLINK temp;
for(temp=head;temp->next!=NULL;temp=temp->next);
temp->next=p;
p->next=NULL;
}
fclose(fp);
return head;
*/
FILE *fp=fopen("/home/lifulin/5","r");
if(fp==NULL)
{
printf("文件没打开\n");
return;
}
rewind(fp);
pLINK temp=(pLINK)malloc(sizeof(LINK));
printf("%d\n",temp->data.num);
fread(&temp->data,sizeof(Student),1,fp);
if(temp==NULL)
{
printf("没有文件读出\n");
return;
}
pLINK head=(pLINK)malloc(sizeof(LINK));
head->next=temp;
pLINK temp1=temp;
temp=(pLINK)malloc(sizeof(LINK));
while(fread(temp,sizeof(Student),1,fp)==1)
{
// printf("%d\n",temp->data.num);
temp1->next=temp;
temp1=temp;
temp=(pLINK)malloc(sizeof(LINK));
}
printf("读取成功\n");
return head;
}
void blueSort1(pLINK head)
{
/* pLINK i=head;
for(i=head;
*/
int i=0;
int length=getListLength(head);
for(i=0;i<length-1;i++)
{
pLINK j;
for(j=head->next;j->next!=NULL;j=j->next)//这里较原来的冒泡排序多算了几次。
{
if(j->data.num>j->next->data.num)
{
Student stu=j->data;
j->data=j->next->data;
j->next->data=stu;
}
}
}
printf("排序成功\n");
}
void blueSort2(pLINK head)
{
pLINK p1;
for(p1=head->next;p1->next!=NULL;p1=p1->next)//不是p1!=NULL,因为p2=p1->next
{
pLINK p2;
for(p2=p1->next;p2!=NULL;p2=p2->next)
{
if(p1->data.num>p2->data.num)
{
Student temp=p1->data;
p1->data=p2->data;
p2->data=temp;
}
}
}
printf("排序成功\n");
}
void selectSort(pLINK head)
{
pLINK i;
for(i=head->next;i->next!=NULL;i=i->next)
{
pLINK j;
pLINK min=i;
for(j=i->next;j!=NULL;j=j->next)
{
if(min->data.num>j->data.num)
{
min=j;
}
}
if(min!=i)
{
Student temp=min->data;
min->data=i->data;
i->data=temp;
}
}
printf("排序成功\n");
}
pLINK offListSort(pLINK head)
{
pLINK first=(pLINK)malloc(sizeof(LINK));
first->next=NULL;
pLINK tail=first;
while(head->next!=NULL)
{
pLINK temp;
pLINK minf=head;
pLINK min=head->next;
for(temp=head->next;temp->next!=NULL;temp=temp->next)
{
if(min->data.num>temp->next->data.num)//神来之笔!而且与前面temp=head->next呼应。
{
minf=temp;
min=temp->next;
}
}
minf->next=min->next;
/* for(temp=head->next;temp!=NULL;temp=temp->next)//头删
{
if(min->data.num>temp->data.num)
{
Student stu=min->data;
min->data=temp->data;
temp->data=stu;
}
}
head->next=min->next;
*/
/*
pLINK temp1;
for(temp1=first;temp1->next!=NULL;temp1=temp1->next);
temp1->next=min;
min->next=NULL;*/
tail->next=min;
min->next=NULL;
tail=tail->next;
}
free(head);
head=NULL;
/* printf("排序成功\n");
pLINK temp2;
for(temp2=first->next;temp2!=NULL;temp2=temp2->next)
{
printf("name=%s,num=%d\n",temp2->data.name,temp2->data.num);
}*/
return first;
}
pLINK sort(pLINK head)
{
if(head==NULL||head->next==NULL)
{
printf("无数据可排序\n");
return;
}
int select;
while(1)
{
printf("===========\n");
printf("1.冒泡1\n");
printf("2.冒泡2\n");
printf("3.选择\n");
printf("4.脱链\n");
printf("5.返回上一层\n");
printf("===========\n");
scanf("%d",&select);
getchar();
switch (select)
{
case 1:
blueSort1(head);
break;
case 2:
blueSort2(head);
break;
case 3:
selectSort(head);
break;
case 4:
head=offListSort(head);
break;
case 5:
return head;
default:
break;
}
}
return head;
}
int main()
{
//创建链表 (创建头节点)
pLINK head=NULL;
// head=(pLINK)malloc(sizeof(LINK));
// head->next=NULL;
int select;
while(1)
{
printf("===========\n\n");
printf("1.创建链表\n");
// printf("2.头插节点\n");
// printf("3.尾插节点\n");
// printf("4.头删节点\n");
// printf("5.尾删节点\n");
// printf("6.删除节点\n");
printf("2.插入节点\n");
printf("3.删除节点\n");
printf("4.打印数据\n");
printf("5.查找学号\n");
printf("6.修改数据\n");
printf("7.退出程序\n");
printf("8.保存文件\n");
printf("9.读取文件\n");
printf("10.数据排序\n");
printf("===========\n\n");
scanf("%d",&select);
getchar();
switch (select)
{
case 1:
head=createList(head);
break;
case 2:
insertData(head);
break;
case 3:
deleteData(head);
break;
case 4:
printData(head);
break;
case 5:
searchData(head);
break;
case 6:
changeData(head);
break;
case 7:
return 0;
case 8:
saveData(head);
break;
case 9:
head=readDataFromFile();
break;
case 10:
head=sort(head);
break;
/* case 5:
tailDeleteData(head);
break;
case 6:
randomDeleteData(head);
break;
case 7:
printData(head);
break;*/
default:
printf("输入错误,请重试\n");
break;
}
}
// head=createList(head);
// headInsertData(head);
//1.头插 数据节点插在头节点的后面
/* pLINK p=(pLINK)malloc(sizeof(LINK));//主函数里可以不用malloc,这里主要
p->next=head->next; //为之后的函数调用做准备。
head->next=p;
printf("请输入学生信息[name num]:\n");
Student stu;
scanf("%s%d",stu.name,&stu.num);//这里%s遇空格和回车就止,故空格和回车
p->data=stu;//神来之笔!可以的。 //都可以。
*/
//2.打印数据
// printData(head);
// pLINK temp=head;
// for(temp=head->next;temp!=NULL;temp=temp->next)
// {
// printf("name=%s,num=%d\n",temp->data.name,temp->data.num);
// }
return 0;
}
网友评论