1. 课题简介和设计要求
孛儿只斤●铁木真(1162年5月31 日-1227年8月25日) ,蒙古帝国可汗,尊号“成吉思汗”意为“拥有海洋四方”。世界史上杰出的政治家、军事家。1206年春天建立大蒙古国.此后多次发动对外征服战争,征服地域西达中亚、东欧的黑海海滨。1227年在征伐西夏的时候去世,之后被密葬。除了一生征战,亦创建蒙古文字、颁布文法、采取开明的宗教政策,是一位在历史上具有颇多争议的枭雄。其后代传承对普通百姓而言,扑朔迷离.....
现在需要设计一个族谱管理系统,使用户能够方便地维护和查询族谱信息
2. 总体设计
根据需求,我设计出以下系统功能:
初始化系统:读取族谱数据文件(txt文件),建立初始族谱。族谱数据文件预先手工建立。
显示族谱:输出族谱。
添加成员:提示输入成员信息,在族谱中添加成员。
修改成员提示输入成员姓名,修改成员信息。
删除成员:提示输入成员姓名,Y从族谱中删除成员。
查询:根据指定条件查询数据并输出。
保存:保存当前族谱数据到文件(txt文件)。
退出:退出系统。
为了实现这些功能,我设计了以下功能菜单:
系统功能模块图如下:
输入5查找时,会有如图所示五种查找方式可选择,界面如下:
3. 详细设计
对详细设计的过程进行介绍,包括:
3.1 结点类型的设计
为了表示族谱信息,我设计了一个Person类,该类包含以下数据项:
姓名:表示该人的姓名。
生年:表示该人的出生年份
卒年:表示该人的死亡年份
父亲:表示该人的父亲,使用一个Person指针表示
孩子:表示该人年龄最小的孩子,使用一个Person指针表示
兄长:表示该人年龄最小的兄长,使用一个Person指针表示。
3.2 采用的逻辑结构
为了满足系统功能的设计要求,我考虑使用树形结构来存储族谱信息。树形结构的每个节点表示一个人,边表示父子关系。这样就可以方便地查询某个人的父亲、孩子等信息。
3.3 采用的存储结构
我选择使用兄弟孩子链式存储结构来实现树形结构。在Person类中定义一个children指针,表示该结点的孩子结点。同时,在Person类中定义一个siblings指针,表示该节点的兄弟节点,并且,为了便于查询该结点的父亲,还定义了一个father指针,表示该结点的父亲。
存储结构示意图:
代码:
3.4 相关算法
为了实现系统的功能,需要设计哪些相关的算法,给出这些算法的思想和处理流程图。
- 初始化系统:读取族谱数据文件(txt文件),建立初始族谱。族谱数据文件预先手工建立。
为了实现这个功能,我定义了一个全局函数impotData,首先使用getline函数读取一行的数据,再将这一行数据根据空格拆分输出到name birth death father四个变量中,然后调用添加函数,将结点添加到树中
- 显示族谱:输出族谱。
由于children指针指向年龄最小的那个孩子,考虑到需要长幼有序进行输出,年长的一般先输出,所以需要先输出当前人的兄长的信息,然后输出当前人的信息,最后输出当前人的子女的信息。代码如下:
参数person指向要输出家谱树的人,参数indentation表示这个人在家谱树中的层级,每个层级输出信息前需要先输出一个tab。
首先使用了一个if语句来判断传入的参数是否为空,如果为空,就返回。
然后递归调用自身函数,并将当前人的兄弟作为参数,层级不变。这样就会先输出所有兄弟的信息。
然后,使用了一个for循环,在每个人的信息前输出层级个tab,表示这个人的层级。最后输出当前人的信息(名字、出生年份和死亡年份)。
最后,再次调用自身函数,并将当前人的子女作为参数,并将层级加1。这样就会输出所有子女的信息。
- 添加成员:提示输入成员信息,在族谱中添加成员。
添加成员要注意长幼有序的问题,children指针必须指向年纪最小的孩子。代码如下:
首先使用了一个if语句来判断这个人是否已经在家谱树中。如果已经在家谱树中了,就输出一条信息。
然后创建一个新的Person对象,并将其保存在指针newPerson中。这个对象的名字、出生日期、死亡日期和父亲指针都被设置为传入的参数。
接下来,使用了一个if语句来判断新添加的人的父亲的名字是否为空。如果为空,则说明新添加的人是家谱树的根结点。如果根结点还没有被设置,则将root指针设置为新创建的Person对象。
否则,就说明新添加的人有一个父亲。那么就使用函数findPerson来查找这个父亲所在的节点,并将查找到的父亲节点保存在指针parent中。
接下来,如果这个父亲没有子女(即parent->children为空指针),则新添加的人成为这个父亲的第一个子女。否则,就需要考虑将新添加的人按照出生日期的顺序插入到父亲的子女链表中。年龄大的在前面,年龄小的在后面。
为了找到新添加的人应该插入的位置,使用一个while循环和一个临时变量temp。每次循环中,都会将temp指向父亲的子女链表中的下一个节点。然后比较这个节点的出生日期和新添加的人的出生日期。如果新添加的人的出生日期比这个节点的出生日期小,则继续循环;否则,就找到了新添加的人应该插入的位置。
最后,将新添加的人插入到这个位置。
- 修改成员提示输入成员姓名,修改成员信息。
需要实现的功能为:首先输入需要修改的成员的姓名 Oname,如果成员不存在,则输出 "查无此人"。如果存在,则输入需要修改的信息,输入birth修改生年,输入death修改卒年。代码如下:
还需要注意的是,如果是 "birth",需要判断生年是否大于当前节点的卒年,如果是 "death",则判断生年是否小于当前节点的卒年。代码如下:
- 删除成员:提示输入成员姓名,Y从族谱中删除成员。
输入成员姓名时,如果不存在该成员,就会提示”查无此人“,并且为了避免误删,删除时需要输入Y确认后才会进行删除操作。
删除成员时,如果这个人有孩子,则孩子也需要全部删除。删除铁木真时,整个族谱都会被删除。
代码如下:
current:指向当前结点的指针,parent:指向当前结点的父亲结点的指针,name:表示要删除的人的名字
首先,如果当前结点current 为空,则直接返回。
如果不为空,则先检查当前结点的名字是不是要删除的人的名字,如果不是则先到当前结点的孩子里查找,然后再到兄弟里查找。
直到当前结点就是要删除的结点。然后判断这个结点是不是根结点,如果是直接将根结点置为空就可以。否则,则需要先找到指向这个结点的指针。
这里有两种情况,一是要删的人是其父亲最小的孩子,此时让父亲的children指针指向要删结点的兄弟结点就好了。
如果不是最小的孩子,则需要先找到指向待删的人的指针temp,然后让temp的兄弟指针指向待删的人的兄弟指针就可以了。
- 查询:根据指定条件查询数据并输出。
按姓名查找:
从根结点开始,先查看当前结点是不是要找的成员,然后递归地到孩子和兄弟里查找。代码如下:
按代查找成员:
person:指向当前结点的指针,generation:要搜索的代数,current:当前结点的代数
如果当前结点为空,则直接返回。如果当前结点的代数已经大于要搜索的代数,也直接返回。否则,如果当前结点的代数等于要搜索的代数,则输出当前结点的名字。接下来,递归地去兄弟和孩子中查找代数为generation的结点。以当前结点的子结点为根结点搜索时,代数要加1,以当前结点的兄弟结点为根结点搜索时,代数不变。代码如下:
查询孩子:
输入某人名字,输出这个人的所有孩子。
首先需要检查这个人是否存在,不存在需要提示此人不存在。
如果这个人存在,先定义一个指针child指向此人的最小的孩子,将其输出,如果他最小的孩子有兄长,则再将其兄长也输出,直到输出所有兄长为止。
查询父亲:
首先输入名字,一样的需要先判断此人是否存在,再判断此人的父亲是否存在,如果存在直接输出其父亲的名字就好了。
查询兄弟:
输入姓名,输出此人的所有兄弟。
只需要先找到此人的父亲,然后除了此人外,输出父亲的最小的孩子children和children的所有兄长即可。
- 保存:保存当前族谱数据到文件(txt文件)。
首先写入当前结点信息,然后再递归地写入当前结点的所有兄长的信息,最后再写入当前结点的孩子结点的信息。
这样可以有效地避免父亲还没有写入文件,就先写入了孩子的情况。代码如下:
- 退出:退出系统。
输出感谢使用再见后退出while循环,代码如下:
4. 编码设计
对已经确定采用的存储结构准备设计哪些类,对涉及到的每一个类给出以下内容:
- 类名
Person类,FamilyTree类
- 数据成员的定义
Person类:
姓名:表示该人的姓名。
生年:表示该人的出生年份
卒年:表示该人的死亡年份
父亲:表示该人的父亲,使用一个Person指针表示
孩子:表示该人年龄最小的孩子,使用一个Person指针表示
兄长:表示该人年龄最小的兄长,使用一个Person指针表示。
FamilyTree类:
表示家谱的根结点
Person *root; // 家谱的根结点
- 成员函数的设计
具体代码及实现如上述3.4
FamilyTree类:
Person类:
分别为构造函数和输出打印函数
测试
5.1 测试用例
- 添加成员测试用例:
(1)姓名:斡儿答 1204 1280 术赤
(2)姓名:阔端 1206 1251 窝阔台
- 修改成员测试用例:
(1)姓名:扯忽
(2)姓名:旭烈兀 修改生年为:1214
(3)姓名:旭烈兀 修改卒年为:1210
3:删除成员测试用例:
(1)姓名:扯忽
(2)姓名:忽察
(3)姓名:旭烈兀
(4)姓名:铁木真
4:查询测试用例:
(1)按姓名查询:扯忽,忽必烈
(2)按代查询:1,7,3,9
(3)查询孩子:扯忽,拖雷
(4)查询父亲:扯忽,明理帖木儿
(5)查询兄弟:扯忽,忙哥剌
5.2 程序运行结果
1、显示族谱
- 添加成员
- 修改成员
- 删除成员
删除前:
删除后:
- 查找成员
不保存,回到初始化的状态。
- 按姓名查找成员
- 按代查找成员
- 查询孩子
- 查询父亲
- 查询兄弟
- 保存
- 退出系统
6.收获与体会
做完这个项目让我更加了解了使用孩子兄弟链表的存储方式的树的结构和操作,更加熟练地掌握了树的增删改查等操作。
在这个项目中,我也学到了如何使用文件来读取并存储家庭树信息,并对文件操作的相关知识掌握地更牢固了。
做完这个项目之后,我对自己的编码能力有了更大的信心,并且更加了解了自己的不足之处,有了进一步提高的动力。
7.参考文献
无