数据结构二叉树家谱管理系统.docx
《数据结构二叉树家谱管理系统.docx》由会员分享,可在线阅读,更多相关《数据结构二叉树家谱管理系统.docx(31页珍藏版)》请在冰豆网上搜索。
数据结构二叉树家谱管理系统数据结构二叉树家谱管理系统摘要在本次家谱课程设计中采用二叉树来表示家谱关系,由于在家谱中每个家族成员的子女不止一个,而双亲只有一个,所以采用二叉树结构来描述家族成员之间的关系。
在家谱课程设计中还用到单链表,在设计中要将二叉树存储在文件中,最终要读取文件中的记录,要将文件中的数据还原到内存中组成二叉树结构,而文件中元素与元素之间的结构是线性,而且直接对文件中的数据操作很不方便,所以将文件中的元素存储在单链表中,再对单链表操作还原成内存中的二叉树。
在对家谱的文件操作中,为了还原家谱方便,采用按层遍历的顺序保存二叉树中各结点的信息,在层次遍历中,使用队列来实现二叉树的层次遍历。
本家谱主要包括两个模块,第一个是文件操作功能模块,此模块实现了家谱记录输入、读取存盘记录、清除家谱存盘记录、添加成员、存盘、修改家谱成员信息、删除家谱成员等七大功能;第二个是家谱操作功能模块,实现了查找某人记录、查找某人的孩子、查找某人的祖先、用括号表示法输出家谱、用凹入表示法输出家谱等六大功能。
关键词:
BinarytnodesavesearchaddRecorddelchangeclear本程序功能框架图1项目总体设计1.1需求分析
(1)文件操作功能:
记录输入、记录输出、清除全部文件记录和将家谱记录存盘。
初始化:
用户可输入一个家族的族谱,输入完成之后可保存在文件中。
在其后的操作中,可从文件里读取族谱信息、增加新的家族成员、修改已有的家族成员、删除已存在的家族成员、可清除所有的家族成员信息。
操作完成之后可保存在文件中。
(2)家谱操作功能:
用括号表示法和凹入法输出家谱二叉树,并能查找某人的配偶、所有孩子、所有祖先、兄弟等功能。
1.2系统功能模块设计:
一问题描述本课程设计的主要问题是选择一种数据结构来描述家谱中家族成员之间的关系,在此数据结构上加之一些操作,选用特定的算法来实现家谱操作的功能和文件操作的功能。
二基本要求设计要求:
编写一个程序,采用一棵二叉树表示一个家谱关系。
具体要求:
(1)文件操作功能:
记录输入、记录输出、清除全部文件记录和将家谱记录存盘。
(2)家谱操作功能:
用括号表示法和凹入法输出家谱二叉树,并能查找某人所有的儿子,查找某人的所有祖先。
三概要设计1数据结构的设计在家谱课程设计中采用二叉树来表示家谱关系,由于在家谱中每个家族成员的子女不止一个,而双亲只有一个,所以采用二叉树结构来描述家族成员之间的关系。
在家谱课程设计中还用到单链表,在设计中要将二叉树存储在文件中,最终要读取文件中的记录,要将文件中的数据还原到内存中组成二叉树结构,而文件中元素与元素之间的结构是线性,而且直接对文件中的数据操作很不方便,所以将文件中的元素存储在单链表中,再对单链表操作还原成内存中的二叉树。
在对家谱的文件操作中,为了还原家谱方便,采用按层遍历的顺序保存二叉树中各结点的信息,在层次遍历中,使用队列来实现二叉树的层次遍历。
2算法的设计本设计从总体上主要分2个模块,分别是家谱操作模块和文件操作模块。
家谱操作模块:
1)Binarytnode*Binarytree:
bulid(Binarytnode*p,intnum);/输入家谱,形参p为二叉树根结点的地址,形参num为结点的编号,建立二叉树并返回这棵二叉树的根结点的地址。
算法:
首先先建立一个二叉树结点,提示用户输入该结点的姓名,将用户输入的姓名写入到该结点的成员变量name中,将形参num写入到该结点的成员变量number中,将双亲结点的地址赋给结点的成员变量的双亲指针,提示用户是否有左孩子,若有左孩子则递归调用通过形参将双亲结点的地址传递过去,递归调用返回的地址赋给它的双亲结点的左孩子指针,右孩子的建立同理可得,通过递归来建立整棵二叉树。
2)Binarytnode*Binarytree:
searchRecord(stringname);/形参name要查找的姓名,按姓名查找某人记录,若找到记录则返回该节点的地址,反之,则返回一个空指针。
算法:
通过层次遍历来和每个结点的成员变量name比较若姓名匹配则返回该节点的地址。
首先判断二叉树的根结点是否为为空,若为空则不进行查找。
反之,则将根结点入队列,然后进入一个循环体,使循环为真的条件是队列不为空,循环体语句是取队头元素,然后对该元素的判断它的成员变量name是否与查找的姓名相匹配。
若匹配则返回该结点的地址,若不匹配,则删除队头元素将它的左右孩子入队列(左右孩子不空的情况下),进行循环。
若直到遍历完整棵子树都还没找到,则返回一个空指针。
3)voidBinarytree:
searchChild(stringname);/形参name要查找的姓名,按姓名查找某人的孩子,若找到记录则显示该节点孩子的姓名,反之,则提示未找到信息。
算法:
调用Binarytnode*searchRecord(stringname)函数查找与参数name相匹配的记录,然后判断其返回的地址是否为空,若为空则不进行查找其子女,若不为空,则输出其左右孩子(左右孩子不空的情况下)。
4)voidBinarytree:
searchParent(stringname);/形参name要查找的姓名,按姓名查找某人的祖先,若找到记录则按辈份从小到大显示该节点的祖先,反之,则提示未找到信息。
算法:
调用Binarytnode*searchRecord(stringname)函数查找与参数name相匹配的记录,然后判断其返回的地址是否为空,若为空则不进行查找其双亲,若不为空,则用循环通过双亲指针按辈分从小到大输出双亲的姓名,直到双亲的地址为空,结束循环。
5)voidBinarytree:
addRecord(stringparent,stringname);/插入家族成员记录,形参parent为要插入的家族成员的双亲姓名,形参name为要插入家族成员的姓名。
算法:
调用Binarytnode*searchRecord(stringname)函数查找与参数name相匹配的记录,然后判断其返回的地址是否为空,若为空则不进行插入操作,若不为空,则判断其是否左子树是否为空,若为空则建立一个结点将参数name赋给结点的成员变量name中去,然后通过它的双亲结点的编号计算出它的编号为2*number,将编号、双亲的地址分别赋给它的成员变量number、parent。
若不为空,再判断它的右子树是否为空,若为空则按建立左孩子的同样的方法建立右孩子。
若还为空,则说明左右子树都不为空,则不允许添加记录。
6)voidBinarytree:
change(stringname);/修改家族成员的信息,行参name为要修改的家族成员的姓名。
算法:
调用Binarytnode*searchRecord(stringname)函数查找与参数name相匹配的记录,然后判断其返回的地址是否为空,若为空则不进行修改操作,若不为空,则将参数name赋给它的成员变量name中。
7)voidBinarytree:
del(stringname);/删除家族成员,行参为要删除的家族成员的姓名。
算法:
调用Binarytnode*searchRecord(stringname)函数查找与参数name相匹配的记录,然后判断其返回的地址是否为空,若为空则不进行删除操作,若不为空,则判断其左右子树是否为空,若左右子树都为空,则将其双亲结点的对应的左或右孩子指针赋为空,释放其结点的空间。
反之,则不删除该结点。
该函数只允许删除叶子结点。
8)Binarytnode*Binarytree:
load(node*head,Binarytnode*parent)/读取存盘记录,将单链表还原为二叉树,返回该二叉树的根结点的地址。
算法:
通过递归调用来重建二叉树。
首先,调用函数node*node:
read(),该函数的作用是将文件中二叉树的信息存放在单链表中,返回单链表的头指针的地址。
若返回的指针为空,则,不进行读取存盘记录操作,若不空,则建立一个二叉树结点,取单链表的的一个节点,将其结点的信息写入到二叉树结点的相应的成员变量中,通过编号计算出该左孩子的的编号(由于孩子的编号肯定小于双亲的编号,而编号在单链表是从小到大排列的)从当前单链表的结点往下寻找编号相匹配的结点若找到,则将该结点的地址和二叉树结点的地址通过函数的参数递归调用该函数返回的地址赋给二叉树的左孩子指针,右子树的建立同左子树的建立同理。
建立完整个二叉树后,返回该二叉树根结点的地址。
9)voidBinarytree:
displaytree1(Binarytnode*root)/括号法显示,形参root为根结点的的地址。
算法:
用递归来实现二叉树的括号法表示。
首先,判断二叉树根结点是否为空,若为空,则不显示二叉树,若不为空,则显示根结点的数据,输出(,再递归调用显示它的左孩子(左孩子不空的情况下),然后输出,再递归调用显示它的右孩子(右孩子不空的情况下),然后输出)。
10)voidBinarytree:
displaytree2(Binarytnode*root,intn);/凹入法显示,形参root为根结点的地址,行参n为缩进量。
算法:
用递归来实现二叉树的凹入法表示。
首先,判断二叉树是否为空,若为空,则不显示二叉树,设置输出宽度(由形参n决定),递归显示它的左孩子输出宽度为它双亲结点的输出宽度的2倍,换行,通力递归显示它的右孩子,换行。
文件操作模块:
1)voidBinarytree:
save(Binarytnode*root)/存盘,将家谱保存到文件中,行参为二叉树根结点的地址。
算法:
为了还原二叉树方便采用按层次遍历(其记录按编号由从小大到大的顺序存储在文件中)的顺序来保存二叉树的信息。
首先,先清空原来文件的内容,判断二叉树根结点的是否为空,若为空,则不进行存盘操作,若不为空,将根结点入队列,进入循环,循环为真的条件是对列为空,循环体为,取队头元素,将对头元素的姓名和编号写入文件,然后删除对头元素,再将其左子树和右子树入队列(若左右子树存在的情况下)。
当循环结束的时候,整棵二叉树都保存在文件中(遍历结束)。
2)voidBinarytree:
clear();/清除文件内容。
算法:
删除存档文件jiapu.txt。
并建立一个空文件jiapu.txt。
2)node*node:
read();/将文件中二叉树的信息存放在单链表中,返回单链表的头指针的地址算法:
首先,判断文件是否为空,若文件为空,则不进行读取操作,返回一个空指针若不空,则将二叉树按文件中的排列顺序存储在单链表中,并返回该单链表的头指针的地址。
2详细实现步骤、流程图、代码:
2.1实现步骤:
1)收集资料2)概要设计、详细设计3)编码4)测试2.2流程图1)主菜单代码:
#includeBinarytree.h#include#includevoidmenu()coutendl;cout*欢迎使用家谱管理系统*endl;cout*endl;cout*&文件操作功能&家谱操作功能&*endl;cout*endl;cout*家谱记录输入-1*查找某人记录-8*endl;cout*读取存盘记录-2*查找某人的孩子-9*endl;cout*清除家谱存盘记录-3*查找某人的祖先-c*endl;cout*添加成员-4*用括号表示法输出家谱-k*endl;cout*存盘-5*用凹入表示法输出家谱-n*endl;cout*修改家谱成员信息-6*endl;cout*删除家谱成员-7*endl;cout*endl;cout*显示菜单-m*endl;cout*退出-0*endl;coutendl;voidmain()chars;menu();/显示菜单stringname,str,name2;Binarytreetree;/建立一个二叉树对象nodef;/建立一个单链表while
(1)cout请选择相应功能的序号按enter进入操作:
str;if(str.size()!
=1)s=m;elses=str0;/用于判断用户是否有错误操作switch(s)case1:
cout输入姓名:
;tree.outroot()=tree.bulid(NULL,1);break;/输入家谱case2:
if(f.read()=NULL)cout文件内容为空!
无法读取数据!
endl;/判断单链表是否为空elsetree.outroot()=tree.load(f.read();cout家谱已载入!
endl;break;/将文件中内容还原为二叉树case8:
coutname;Binarytnode*p;p=tree.searchRecord(name);if(p!
=NULL)coutGetname()endl;break;/查找某人的信息case9:
coutname;tree.searchChild(name);break;/查找某人孩子的信息casec:
coutname;tree.searchParent(name);break;/查找某人祖先信息casek:
tree.displaytree1(tree.outroot();coutendl;break;/用括号法表示casen:
tree.displaytree2(tree.outroot(),0);break;/用凹入法表示case5:
if(tree.outroot()=NULL)cout你没有输入家谱记录!
无法保存家谱记录!
endl;elsetree.save(tree.outroot();break;/家谱保存case3:
tree.clear();cout家谱记录清除成功!
endl;break;/删除记录文件casem:
menu();break;case4:
if(tree.outroot()!
=NULL)cout请输入要添加成员的姓字:
name;cout请输入该成员双亲的姓名:
name2;tree.addRecord(name2,name);elsecout家谱记录为空!
无法完成添加操作!
endl;break;case6:
if(tree.outroot()!
=NULL)cout输入要修改成员的姓名:
name;tree.change(name);/调用elsecout家谱记录为空!
无法完成修改操作!
endl;break;case7:
if(tree.outroot()!
=NULL)cout输入要删除成员的姓名:
name;tree.del(name);/调用删除函数elsecout家谱记录为空!
无法完成删除操作!
endl;break;case0:
exit(0);/退出default:
cout错误操作!
name;/输入姓名m-Getname()=name;m-parent()=p;/存放双亲结点的地址m-Getnumber()=num;/获取节点的编号coutGetname()n;if(n=Y|n=y)/判断是否有右子树cout输入他左孩子的姓名:
left()=bulid(m,2*num);/递归调用建立左子树elsem-left()=NULL;/无左子树则左孩子指针赋为空coutGetname()是否有右孩子(Y/N):
n;if(n=Y|n=y)/判断是否有左子树cout输入他右孩子的姓名:
right()=bulid(m,2*num+1);/递归调用建立右子树elsem-right()=NULL;/无左子树则左孩子指针赋为空returnm;3)添加成员主要实现代码voidBinarytree:
addRecord(stringparent,stringname)/(功能4的主要实现函数)/插入家族成员记录,形参parent为要插入的家族成员的双亲姓名,形参name为要插入家族成员的姓名Binarytnode*q;q=searchRecord(parent);if(q!
=NULL)Binarytnode*p;/定义指针p=newBinarytnode;if(q-left()=NULL)/左子树q-left()=p;p-Getname()=name;/新加结点的值p-Getnumber()=2*(q-Getnumber();/新加结点编号p-parent()=q;cout添加成功!
right()=NULL)/右子树操作q-right()=p;p-Getname()=name;p-Getnumber()=2*(q-Getnumber()+1;p-parent()=q;cout添加成功!
right()!
=NULL&q-left()!
=NULL)/左子树不等于空值cout无法添加成员!
endl;4)存盘主要实现代码:
voidBinarytree:
save(Binarytnode*root)/以层次遍历的顺序保存二叉树各结点(功能5的主要实现函数)fstreamoutfile;/定义文件outfile.open(jiapu.txt,ios:
out);outfile.close();system(deljiapu.txt);/清空文件内容queueQ;/Q为队列,队列元素是二叉树结点指针Binarytnode*p;if(root!
=NULL)Q.push(root);/根结点入队列while(!
Q.empty()/判断文件p=Q.front();/取队头元素outfile.open(jiapu.txt,ios:
app|ios:
out);if(!
outfile)cout文件不能打开!
endl;abort();outfileGetname()Getnumber()left()!
=NULL)Q.push(p-left();/左孩子结点入队列if(p-right()!
=NULL)Q.push(p-right();/右孩子节点入队列cout已保存到文件!
endl;5)修改家谱成员信息主要实现代码:
voidBinarytree:
change(stringname)/修改家谱成员的姓名(功能6的主要实现函数)stringstr;Binarytnode*p;p=searchRecord(name);if(p!
=NULL)cout请输入修改后的姓名:
str;p-Getname()=str;cout记录修改成功!
left()=NULL&p-right()=NULL)q=p-parent();if(q-left()=p)q-left()=NULL;elseq-right()=NULL;deletep;cout记录已删除!
Getnext();p-Getname()=head-Getname();p-Getnumber()=head-Getnumber();p-parent()=parent;while(m!
=NULL&m-Getnumber()!
=2*(head-Getnumber()m=m-Getnext();if(m!
=NULL)p-left()=load(m,p);/用递归建立左子树elsep-left()=NULL;m=head-Getnext();while(m!
=NULL&m-Getnumber()!
=2*(head-Getnumber()+1)m=m-Getnext();if(m!
=NULL)p-right()=load(m,p);/用递归建立右子树elsep-right()=NULL;returnp;8)查找某人记录主要实现代码:
Binarytnode*Binarytree:
searchRecord(stringname)/用层次遍历来查找某个记录(功能8的主要实现函数)queueQ;/Q为队列,队列元素是二叉树结点指针Binarytnode*p;if(this-outroot()!
=NULL)Q.push(this-outroot();/根结点入队列while(!
Q.empty()p=Q.front();/取队头元素if(p-Getname().compare(name)=0)/判断是否找到结点的值相同returnp;Q.pop();/删除队头元素if(p-left()!
=NULL)Q.push(p-left();/左孩子结点入队列if(p-right()!
=NULL)Q.push(p-right();/右孩子节点入队列cout未找到相匹的姓名!
left()!
=NULL)coutGetname()的左孩子的姓名为:
endl;coutleft()-Getname()endl;/显示它的左孩子elsecoutGetname()无左孩子!
right()!
=NULL)coutGetname()的右孩子的姓名为:
endl;coutright()-Getname()endl;/显示它的右孩子elsecoutGetname()无右孩子!
parent()=NULL)cout此人没有双亲!
endl;elsecout此人的祖先按辈份从小到大排序为:
parent();while(p!
=NULL)coutGetname()parent();/指针移动到它的双亲节点coutendl;11)括号输出法主要实现代码:
voidBinarytree:
displaytree1(Binarytnode*root)/用递归来实现括号法表示(功能k的主要实现函数)if(root!
=NULL)coutGetname();/输出根结点if(root-right()!
=NULL|root-left()!
=NULL)/判断左子树或者右子树是否为空coutleft();coutright();cout);12)用凹入法输出家谱主要实现代码:
voidBinarytree:
displaytree2(Binarytnode*root,intn)/用递归来实现凹入法表示(功能n的主要实现函数)if(root!
=NULL)coutsetw(3*n)Getname()left(),n+3);displaytree2(root-right(),n+3);13)清除存盘记录主要实现代码:
voidBinarytree:
clear()/(功能3的主要实现函数)fstreamfile;file.open(jiapu.txt,ios:
out);/防止用户删除jiapu.txt,造成删除jiapu.txt时造成文件不存在file.close();system(deljiapu.txt);file.open(jiapu.txt,ios:
out);file.close();/清空文件内容(删除文件再建立一个空文件)调试和运行情况简述本程序包括的两个模块,文件操作和家谱操作功能模块,基本实现了家谱记录输入、读取存盘记录、清除家谱存盘记录、添加成员、存盘、修改家谱成员信息、删除家谱成员实现了查找某人记录、查找某人的孩子、查找某人的祖先、用括号表示法输出家谱、用凹入表示法输出家谱等十三个功能。
但在清楚家谱存盘记录的功能有一点缺陷,即执行清除操作后,数据依然保存在内存中,还能输出记录,需要重新启动程序加载记录后方能看到效果。
3.项目总结通过此次数据结构的课程设计,真正达到了学与用的结合,增强了对二叉树方面应用的理解和掌握。
在实验过程中,从需求分析,到概念设计,懂得了不少有关项目建设的知识,包括,插入、删除、修改、查询之间的联系,都用到很多关键的知识点。
整个程序和以往所编的程序较大的区别就是要求从文件读入信息,文件是C+语言中较为重要的部分,通过课程设计,加深了对文件知识的掌握。
我的课程设计原先是只能从固定的一个文件读入图的信息,经过修改,它可以通过直接代码执行来提取文件,使程序更加灵活,功能更加完善。
在学习过程中,我也能过上网查了不少资料,学以致用,自我创新,独立完成了这份自己的报告,从中在学到用,从用又到学,不