四网合一的网站工具
三.家谱管理系统(必做)(树)
[问题描述]
实现具有下列功能的家谱管理系统。
[基本要求]
(1)输入文件以存放最初家谱中各成员的信息,成员的信息中均应包含以下内容:姓名、出生日期、婚否、地址、健在否、死亡日期(若其已死亡),也可附加其它信息、但不是必需的。
(2)实现数据的文件存储和读取。
(3)以图形方式显示家谱。
(4)显示第n 代所有人的信息。
(5)按照姓名查询,输出成员信息(包括其本人、父亲、孩子的信息)。
(6)按照出生日期查询成员名单。
(7)输入两人姓名,确定其关系。
(8)某成员添加孩子。
(9)删除某成员(若其还有后代,则一并删除)。
(10)修改某成员信息。
(11)要求建立至少40个成员的数据,以较为直观的方式显示结果,并提供文稿形式以便检查。
(12)界面要求:有合理的提示,每个功能可以设立菜单,根据提示,可以完成相关的功能要求。
(13)存储结构:根据系统功能要求自行设计,但是要求相关数据要存储在数据文件中。测试数据:要求使用1、全部合法数据;2、局部非法数据。进行程序测试,以保证程序的稳定。
数据结构
用到的数据结构为二叉树、二进制文件的读写。
算法设计思想
运用左孩子、右兄弟的二叉树形式来存储家谱中成员信息,结构体中储存着每个人的基本信息,包括,1表示存在,0表示不存在,用于建树、几代人、姓名、出生日期、死亡日期 、婚否 (0未婚,1已婚)、健在否(0去世,1健在)、地址 ,第一次输入家谱信息时,根据先序遍历的算法进行建树,此后在运行程序时便不需要再输入全部信息了,因为程序已将家谱信息存入二进制文件中,直接读取即可,显示n代人的基本信息,只需先序遍历二叉树,如果该成员是第n代人,输出其基本信息,按照姓名查询,输出成员信息(包括其本人、父亲、孩子的信息),先序遍历返回该结点指针并输出本人基本信息,根据其parent指针输出其父亲的基本信息,输出其左孩子及其左孩子的全部右兄弟指针的基本信息就是其全部孩子,按照出生日期查询成员名单也是通过先序遍历进行查找。输入两人姓名,确定其关系。先序遍历查找到两人的指针并返回,先判断两人是否是同一代人,在判断两人的父亲指针及其父亲指针的父亲指针是否相同来判断两人的关系。某成员添加孩子。先序遍历查找到此人的指针并返回,先判断此人左孩子指针是否为空,如果为空则直接将其添加为左孩子,如果不为空则判断其左孩子的右兄弟结点,一直找到为空的右兄弟结点,插入到此处。删除某成员(若其还有后代,则一并删除)。先序遍历查找到此人的指针并返回,如果此节点有右兄弟的话那么直接将右兄弟结点插入到指向此节点的右兄弟结点,利用后序遍历的二叉树销毁算法释放掉此节点及其孩子结点的指针信息。修改某成员信息,先序遍历查找到此人的指针并返回,直接进行修改。
测试数据和结果
下图是经过上图修改后的家谱信息
源代码
#include <stdlib.h>
#include <stdlib.h>
#include<iostream>
#include<fstream>
#include<string.h>
#include<math.h>
using namespace std; typedef struct memberinfo
{bool exist; //1表示存在,0表示不存在,用于建树 int num; //几代 char name[20]; //姓名 char birthday[20]; //出生日期 char deathday[20]; //死亡日期 bool marry; //婚否 (0未婚,1已婚) bool live; //健在否(0去世,1健在) char address[100]; //地址
}Info;
typedef struct Genealogy
{Info member;struct Genealogy *lchild,*rbrother,*parent;
}Genealogy,*genealogy;Info person;
Info people; void PreOrderTraverseWrite_Dat(fstream &file,Genealogy* T);
void PreOrderTraverseWrite_Txt(fstream &file,Genealogy* T);
void FindChild_txt(Genealogy* p,fstream &file);
void writefile(fstream &p) //将家谱信息写进二进制文件
{p.open("族谱.dat",ios::out|ios::binary);if(p.fail()){cout<<"打开文件失败!";return ;} Info father;char a[20], b[20], c[20], d[20];int n;char l;do{cin>>father.exist;cin.ignore();if(father.exist==1){cin>>father.num;cin.ignore();cin.getline(a, 20);cin.getline(b, 20);cin.getline(c, 20);strcpy(father.name,a);strcpy(father.birthday,b);strcpy(father.deathday,c);cin>>father.marry;cin.ignore();cin>>father.live;cin.ignore();cin.getline(d, 20);strcpy(father.address,d);}else{strcpy(father.name,"NULL");strcpy(father.birthday,"NULL");strcpy(father.deathday,"NULL");father.marry=1;father.live=1;father.num=0;}p.write((char*)&father,sizeof(father));cin>>l;cin.ignore();}while(l=='y');p.close();
}
void Create(fstream &data,genealogy &T)
{data.read((char*)&person,sizeof(person));if(person.exist==0) T=NULL;else{T=(Genealogy*)malloc(sizeof(Genealogy));if(!T) exit(0);T->member.exist=person.exist;T->member.num=person.num;strcpy(T->member.name,person.name);strcpy(T->member.birthday,person.birthday);strcpy(T->member.deathday,person.deathday);strcpy(T->member.address,person.address);T->member.marry=person.marry;T->member.live=person.live;Create(data,T->lchild);Create(data,T->rbrother);}
}
void output_impl(genealogy T, bool left, string const& indent)
{if (T->rbrother ){output_impl(T->rbrother , false, indent + (left ? "| " : " "));}cout << indent;cout << (left ? '\\' : '/');cout << "-----";cout << T->member.name << endl;if (T->lchild ){output_impl(T->lchild , true, indent + (left ? " " : "| "));}
}
void output(genealogy T)
{if (T->rbrother ){output_impl(T->rbrother , false, "");}cout << T->member.name << endl;if (T->lchild ){output_impl(T->lchild , true, "");}
}void CreateGenealogy(fstream &data,genealogy &T)
{data.open("族谱.dat",ios::in|ios::binary);Create(data,T);data.close();
}
void setparent(genealogy &T,Genealogy* parent)
{T->parent=parent;if(T->lchild!=NULL)setparent(T->lchild,T);if(T->rbrother!=NULL)setparent(T->rbrother,parent);
}
void Show(genealogy T,int n)
{if(T!=NULL){if(T->member.num==n){cout<<"姓名:"<<T->member.name<<endl;cout<<"日期:"<<T->member.birthday<<"-"<<T->member.deathday<<endl;if(T->member.marry==1) cout<<"婚否:"<<"已婚"<<endl;else cout<<"婚否:"<<"未婚"<<endl;if(T->member.live==1) cout<<"健在否:"<<"健在"<<endl;else cout<<"健在否:"<<"不健在"<<endl;cout<<"地址:"<<T->member.address<<endl;cout<<"------------------------------------------------------------"<<endl;}Show(T->lchild,n);Show(T->rbrother,n);}
}
void Menu()
{cout << " 家谱管理系统" << endl;cout << " 主菜单" << endl;cout << "1:输入家谱信息" << '\n' << "2:创建家谱" << '\n' << "3:显示第n代所有人的信息" << '\n' << "4:按照姓名查询,输出成员信息(包括其本人、父亲、孩子的信息)" << '\n'<< "5:按照出生日期查询成员名单" << '\n'<< "6:输入两人姓名,确定其关系" << '\n'<< "7. 某成员添加孩子" << '\n'<< "8. 删除某成员(若其还有后代,则一并删除)" << '\n'<< "9. 修改某成员信息" << '\n'<< "10. 退出系统" <<'\n'<<" 输入选择(1—11): ";}
Genealogy* FindMember(genealogy T,char Name[])
{Genealogy* p=NULL;if(T==NULL) return NULL;else if(strcmp(Name,T->member.name)==0) return T;else{p=FindMember(T->lchild,Name);if(p==NULL)return FindMember(T->rbrother,Name);else return p;}
}
Genealogy* FindMember1(genealogy T,genealogy q)
{Genealogy* p=NULL;if(T==NULL) return NULL;else if(T->rbrother==q) return T;else{p=FindMember1(T->lchild,q);if(p==NULL)return FindMember1(T->rbrother,q);else return p;}
}
//在以t为根的树中找结点p的双亲
Genealogy* findParent(genealogy& t,Genealogy* p){if(t == NULL || p ==NULL) return NULL;Genealogy* q = t->lchild;Genealogy* s;//循长子的兄弟链,递归在子树中搜索 while(q != NULL && q != p){s = findParent(p,q);if( s!= NULL) return s; //找到双亲,返回 q = q->rbrother;}if(q != NULL && q == p) return t; //找到双亲 else return NULL; //未找到
}
void FindChild(Genealogy* p)
{int i=1;if(p->lchild){p=p->lchild;cout<<"第"<<i<<"个孩子信息: "<<endl;cout<<"姓名:"<<p->member.name<<endl;cout<<"日期:"<<p->member.birthday<<"-"<<p->member.deathday<<endl;if(p->member.marry==1) cout<<"婚否:"<<"已婚"<<endl;else cout<<"婚否:"<<"未婚"<<endl;if(p->member.live==1) cout<<"健在否:"<<"健在"<<endl;else cout<<"健在否:"<<"不健在"<<endl;cout<<"地址:"<<p->member.address<<endl;i++;p=p->rbrother;cout<<"------------------------------------------------------------"<<endl;while(p){cout<<"第"<<i<<"个孩子信息: "<<endl;cout<<"姓名:"<<p->member.name<<endl;cout<<"日期:"<<p->member.birthday<<"-"<<p->member.deathday<<endl;if(p->member.marry==1) cout<<"婚否:"<<"已婚"<<endl;else cout<<"婚否:"<<"未婚"<<endl;if(p->member.live==1) cout<<"健在否:"<<"健在"<<endl;else cout<<"健在否:"<<"不健在"<<endl;cout<<"地址:"<<p->member.address<<endl;i++;p=p->rbrother;cout<<"------------------------------------------------------------"<<endl;}}else cout<<"该成员无孩子"<<endl;
}Genealogy* Findmember_birthday(genealogy T,char Birthday[])
{Genealogy* p=NULL;if(T==NULL) return NULL;else if(strcmp(Birthday,T->member.birthday)==0) return T;else{p=Findmember_birthday(T->lchild,Birthday);if(p==NULL)return Findmember_birthday(T->rbrother,Birthday);else return p;}
}
void InsertChild(genealogy &q)
{Genealogy* q1=q;Genealogy* p=(Genealogy*)malloc(sizeof(Genealogy));p->lchild=NULL;p->rbrother=NULL;cout<<"请输入添加孩子的姓名:";cin>>p->member.name;cout<<"请输入添加孩子的出生日期:";cin>>p->member.birthday;p->member.deathday[0]='#';p->member.live=1;p->member.marry=0;cout<<"请输入添加孩子的地址:";cin>>p->member.address;p->member.exist=1;if(q->lchild==NULL){q->lchild=p;p->parent=q;}else{q1=q->lchild;if(q1->rbrother==NULL){q1->rbrother=p;p->parent=q;}else{while(q1->rbrother)q1=q1->rbrother;q1->rbrother=p;p->parent=q;} }p->member.num=q->member.num+1;cout<<"添加成功"<<endl;
}
Genealogy* Destroy(genealogy &q)
{if(q){q->lchild=Destroy(q->lchild);q->rbrother=Destroy(q->rbrother);free(q);return NULL;}
}
void DeleteMember(genealogy &q,genealogy &p)
{p->rbrother=q->rbrother;Destroy(q);cout<<"删除成功"<<endl;
}
void ModifyMemberinformation(genealogy &q)
{int choice;while(true){cout << "1:修改姓名" << '\n' << "2:修改出生日期" << '\n' <<"3.修改死亡日期"<<'\n'<< "4:修改是否健在" << '\n' << "5:修改是否已婚" << '\n'<< "6:修改地址" << '\n'<< "7. 退出系统" <<'\n'<<" 输入选择(1—6): ";cin>>choice;switch (choice){case 1:cout<<"请输入修改后的姓名:";cin>>q->member.name;break;case 2:cout<<"请输入修改后的出生日期:";cin>>q->member.birthday;break;case 3:cout<<"请输入修改后的死亡日期:";cin>>q->member.deathday;break;case 4:cout<<"请输入修改后是否健在 1.是 0.否:";cin>>q->member.live;break;case 5:cout<<"请输入修改后是否已婚 1.是 0.否:";cin>>q->member.marry;break;case 6:cout<<"请输入修改后的地址:"; cin>>q->member.address;break;case 7:return;break;}}
}
int main()
{fstream datafile;int choice;genealogy T;bool judge=true;while(judge==true){Menu();cin>>choice;switch (choice){case 1:cout<<"请输入家谱信息:";writefile(datafile);cout << '\n';break;case 2:CreateGenealogy(datafile,T);Genealogy* Parent;setparent(T,Parent);output(T);cout<<"家谱创建成功"<<endl;cout << '\n' ;break;case 3:int n;cout<<"请输入要查找第几代人的信息:"; cin>>n;cout<<"------------------------------------------------------------"<<endl;Show(T,n); break;case 4:char Name[20];cout<<"请输入要查找人的姓名:"; cin>>Name;Genealogy* p1;p1=FindMember(T,Name);if(p1==NULL) cout<<"查无此人!"<<endl;else{cout<<"------------------------------------------------------------"<<endl;cout<<"本人信息:"<<endl;cout<<"姓名:"<<p1->member.name<<endl;cout<<"日期:"<<p1->member.birthday<<"-"<<p1->member.deathday<<endl;if(p1->member.marry==1) cout<<"婚否:"<<"已婚"<<endl;else cout<<"婚否:"<<"未婚"<<endl;if(p1->member.live==1) cout<<"健在否:"<<"健在"<<endl;else cout<<"健在否:"<<"不健在"<<endl;cout<<"地址:"<<p1->member.address<<endl;cout<<"------------------------------------------------------------"<<endl;cout<<"父亲信息:"<<endl;cout<<"姓名:"<<p1->parent->member.name <<endl;cout<<"日期:"<<p1->parent->member.birthday<<"-"<<p1->parent->member.deathday<<endl;if(p1->parent->member.marry==1) cout<<"婚否:"<<"已婚"<<endl;else cout<<"婚否:"<<"未婚"<<endl;if(p1->parent->member.live==1) cout<<"健在否:"<<"健在"<<endl;else cout<<"健在否:"<<"不健在"<<endl;cout<<"地址:"<<p1->parent->member.address<<endl;cout<<"------------------------------------------------------------"<<endl;FindChild(p1);}break;case 5:char Birthday[20];cout<<"请输入你想要查询成员的生日:";cin>>Birthday;Genealogy* p3;p3=Findmember_birthday(T,Birthday);if(p3==NULL) cout<<"查无此人!"<<endl;else{cout<<"------------------------------------------------------------"<<endl;cout<<"此人信息:"<<endl;cout<<"姓名:"<<p3->member.name<<endl;cout<<"日期:"<<p3->member.birthday<<"-"<<p3->member.deathday<<endl;if(p3->member.marry==1) cout<<"婚否:"<<"已婚"<<endl;else cout<<"婚否:"<<"未婚"<<endl;if(p3->member.live==1) cout<<"健在否:"<<"健在"<<endl;else cout<<"健在否:"<<"不健在"<<endl;cout<<"地址:"<<p3->member.address<<endl;cout<<"------------------------------------------------------------"<<endl;}break;case 6:char Name1[20],Name2[20];cout<<"请输入第一个人的姓名:"; cin>>Name1;cout<<"请输入第二个人的姓名:"; cin>>Name2;Genealogy* q1,*q2;q1=FindMember(T,Name1);if(q1==NULL) {cout<<"家谱中没有第一个人"<<endl;break;} q2=FindMember(T,Name2); if(q2==NULL){cout<<"家谱中没有第二个人"<<endl;break;}if(abs(q1->member.num-q2->member.num)==0){if(q1->parent==q2->parent)cout<<Name1<<"和"<<Name2<<"是亲兄弟"<<endl;elsecout<<Name1<<"和"<<Name2<<"是堂兄弟"<<endl;}if(abs(q1->member.num-q2->member.num)==1){if(q1->member.num>q2->member.num){if(q1->parent==q2)cout<<Name2<<"是"<<Name1<<"的父亲"<<endl;elsecout<<Name2<<"是"<<Name1<<"的叔叔"<<endl;}else{if(q1==q2->parent)cout<<Name1<<"是"<<Name2<<"的父亲"<<endl;elsecout<<Name1<<"是"<<Name2<<"的叔叔"<<endl;}}if(abs(q1->member.num-q2->member.num)==2){if(q1->member.num>q2->member.num){if(q1->parent->parent==q2)cout<<Name2<<"是"<<Name1<<"的爷爷"<<endl;elsecout<<Name2<<"是"<<Name1<<"的堂爷爷"<<endl;}else{if(q1==q2->parent->parent)cout<<Name1<<"是"<<Name2<<"的爷爷"<<endl;elsecout<<Name1<<"是"<<Name2<<"的堂爷爷"<<endl;}}if(abs(q1->member.num-q2->member.num)>=3){if(q1->member.num>q2->member.num){if(q1->parent->parent==q2)cout<<Name2<<"是"<<Name1<<"的祖先"<<endl;elsecout<<Name2<<"是"<<Name1<<"的祖先的兄弟"<<endl;}else{if(q1==q2->parent->parent)cout<<Name1<<"是"<<Name2<<"的祖先"<<endl;elsecout<<Name1<<"是"<<Name2<<"的祖先的兄弟"<<endl;}}break;case 7:char Name3[20];cout<<"请输入被插入成员父亲的姓名:";cin>>Name3;Genealogy* q3;q3=FindMember(T,Name3);if(q3==NULL)cout<<"查无此人!"<<endl;else{InsertChild(q3);}break;case 8:char Name4[20];cout<<"请输入删除成员的姓名:";cin>>Name4;Genealogy* q4,*q5;q4=FindMember(T,Name4);//cout<<q4->member.name<<endl;q5=FindMember1(T,q4);//cout<<q5->member.name<<endl;DeleteMember(q4,q5);break;case 9:char Name5[20];cout<<"请输入要修改信息的成员的姓名:";cin>>Name5;Genealogy* q6;q6=FindMember(T,Name5);ModifyMemberinformation(q6);break;case 10:judge=false;break;case 11:output(T);}} //此行往下的操作是将之前修改过的家谱信息重新写入文件中 fstream file;file.open("族谱.dat",ios::out|ios::binary); person.exist=0; //定义空白变量 person.num=-1;strcpy(person.name,"NULL");strcpy(person.birthday,"000");strcpy(person.deathday,"000");person.marry=-1;person.live=-1;strcpy(person.address,"NULL");PreOrderTraverseWrite_Dat(file,T);file.close();//此行往下的操作是将之前修改过的家谱写入文件"家谱展示.txt"中,可点击查看 fstream File;File.open("家谱展示.txt");PreOrderTraverseWrite_Txt(File,T);File.close();cout<<"ok啦"<<endl;}
void PreOrderTraverseWrite_Dat(fstream &file,Genealogy* T)
{if(T==NULL){file.write((char*)&person,sizeof(person));}if(T!=NULL){people.exist=T->member.exist;people.num=T->member.num;strcpy(people.name,T->member.name);strcpy(people.birthday,T->member.birthday);strcpy(people.deathday,T->member.birthday);people.marry=T->member.marry;people.live=T->member.live;strcpy(people.address,T->member.address);file.write((char*)&people,sizeof(people));PreOrderTraverseWrite_Dat(file, T->lchild);PreOrderTraverseWrite_Dat(file, T->rbrother);}
}
void PreOrderTraverseWrite_Txt(fstream &file,Genealogy* T)
{if(T==NULL){file<<"姓名:空"<<'\n';file<<"此处无人"<<'\n';file<<"------------------------------------------------------------"<<'\n';}if(T!=NULL){people.exist=T->member.exist;people.num=T->member.num;strcpy(people.name,T->member.name);strcpy(people.birthday,T->member.birthday);strcpy(people.deathday,T->member.birthday);people.marry=T->member.marry;people.live=T->member.live;strcpy(people.address,T->member.address);file<<"姓名:"<<people.name<<'\n';file<<"第"<<people.num<<"代人"<<'\n'; file<<"出生日期:"<<people.birthday<<'\n';file<<"死亡日期:"<<people.deathday<<'\n';if(people.marry==1)file<<"婚否:已婚"<<'\n';elsefile<<"婚否:未婚"<<'\n';if(people.live==1)file<<"健在否:健在"<<'\n';elsefile<<"健在否:不健在"<<'\n';file<<"地址:"<<people.address<<'\n';FindChild_txt(T,file);file<<"------------------------------------------------------------"<<'\n';PreOrderTraverseWrite_Txt(file, T->lchild);PreOrderTraverseWrite_Txt(file, T->rbrother);}
}
void FindChild_txt(Genealogy* p,fstream &file)
{int i=1;if(p->lchild){p=p->lchild;file<<"第"<<i<<"个孩子姓名:"<<p->member.name<<'\n';i++;p=p->rbrother;while(p){file<<"第"<<i<<"个孩子姓名:"<<p->member.name<<'\n';i++;p=p->rbrother;}}else file<<"该成员无孩子"<<'\n';
}
/*姓名、出生日期、婚否、地址、健在否、死亡日期*/