南京航空航天大学
数据结构课程设计
家谱管理系统
姓 名:陈学轩
学 号:161110128
班 级:1611101
指导老师:秦小麟
1
目
录
1. 问题陈述…………………………………………………………………………3
2.所采用的数据类型以及数据结构………………………………………………3
3.算法思想……………………………………………………………………………5
4 . 源 程 序 … … … … … … … … … … … … … … … … … … … … … … … … … … 5
5.算法复杂度 ………………………………………………………………………32
6.测试数据和结果 …………………………………………………………………32
7.总结……………………………………………………………………………39
2
1.问题陈述
家谱用于记录某家族历代家族成员的情况与关系。现编制一个家谱资料管理软件,实
现对一个家族所有的资料进行收集整理。支持对家谱的存储、更新、查询等操作。并用计算
机永久储存家族数据,方便随时调用。
2.所采用的数据类型与数据结构
在动手编制程序之前,先要做好程序的规划,包括程序储存数据所用的结构,数据类型
等等,只有确定了数据类型和数据结构,才能在此基础上进行各种算法的设计和程序的编写。
首先是考虑数据类型。在家谱中,家族成员是最基本的组成部分,对于家族管理中,
已经不能再进行细分了,所以选定家族成员作为数据的基本类型,并在程序中定义 Info 的
结构体。其中 Info 的各种属性可以根据需要进行添加或删除,从日常生活应用的角度出发,
制定了 Info 中包含了一下属性:
char name[max_char_num];//姓名
char birthplace[max_char_num];//出生地点
Date birthdate;//结构 date 定义的出生日期
char dead[max_char_num];//是否已死亡
Date deathdate;//结构 date 定义的死亡日期
char sex[max_char_num];//性别
int height;//高度
char occupation[max_char_num];//工作职务
char education[max_char_num];//受教育程度
char top_headship[max_char_num];//最高职位
char parentname[max_char_num];//父亲姓名,用于添加节点时用
int Depth;//二叉树深度,输出二叉树时用
char love[max_char_num];//是否已婚
int N;//代数
char lname[max_char_num];//配偶姓名
3
char lbirthplace[max_char_num];//配偶出生地点
Date lbirthdate;//配偶结构 date 定义的出生日期
char ldead[max_char_num];//配偶是否已死亡
Date ldeathdate;//配偶结构 date 定义的死亡日期
char lsex[max_char_num];//配偶性别
int lheight;//配偶高度
char loccupation[max_char_num];//配偶工作职务
char leducation[max_char_num];//配偶受教育程度
char ltop_headship[max_char_num];//配偶最高职位
并用 Date 结构体定义了年月日,可有效管理人员的出生日期和死亡日期
struct Date
{
};
int year;
int month;
int day;
//年
//月
//日
因为编写的为家谱管理系统,所以整体上我采用了二叉树的结构进行编写,这里运用
的是三叉链表的结构,首次添加的成员做为根节点,以此往下进行分支,在结构体中定义了
一个孩子结点,一个兄弟结点,一个双亲结点,方便各个代数之间的查询和添加,又因为把
配偶的信息与本人的信息存储在了一起,所以没有配偶的结点,查询到配偶也就查询到了本
人,但是这也造成了我无法修改配偶的信息,只能添加新的配偶信息和查看配偶的信息;
typedef struct CSNode
{
Info data;
//个人信息类型结构
CSNode *firstchild,*nextsibling,*parent;
//csnode 的第一个孩子节点,下一个兄弟节点、
}*person;
双亲借点
在家族关系的表示上,我用的是代数来表示,比如最开始的为第一代,第一代的孩子
为第二代,以此类推,因为家族成员都在二叉树上,我遍历一遍二叉树后就可以知道哪个成
4
员是第几代,这样来表示哪个成员是晚辈、长辈或是平辈。
毫无疑问,用指针来处理数据的确是方便直观,但当我要储存数据是,便发现把指针
储存进去是没有作用的,因为当我们下一次读取数据的时候,数据内存地址已经不同了,不
在是我们上次存储数据时的地址,也就是说指针这时已经是没有作用了。要解决这样的问题,
我们必须要在存储数据之前,先家族树序列化,用数组(或者其他可以用数字表示关系的方
法)来存储,并且,再下一次读取数据时,再把数据按照序列号重新组成一个家族树,过程
比较繁复,而且实现起来也不容易。所以我便考虑直接用数组来存储数据,即使是在内存中
也用数组来处理数据间的联系。运用顺序表这个结构虽然不是那么直观,但在查找数据时的
算法设计比较简单容易实现,效率高,而且在内存中的数据可以直接读入到文件中,文件中
的数据也可以直接读入内存,不需要进行转换。所以在衡量的各个方面之后,我决定用数组
来处理数据间的联系。
3.算法思想
1.在保存和显示家谱时,用先序遍历家谱树再进行操作
2.在删除某一人时,则用后序遍历找到该节点并将该结点及其子节点一并删除
3.在进行查找时,用递归算法调用 FindByName()函数进行遍历家谱树
4.在进行各种查询和添加结点时,用名字进行查询时都用递归算法调用 FindByName()函
数进行查找
4.源程序
Genealogy.h 家谱类的头文件,进行家谱类中函数的定义,以及人员的数据存储类型
#define max_char_num 100
#include
#include
#include
#include
#include
struct Date
{
int year;
//年
5
int month;
int day;
//月
//日
};
struct Info
{
char name[max_char_num];//姓名
char birthplace[max_char_num];//出生地点
Date birthdate;//结构 date 定义的出生日期
char dead[max_char_num];//是否已死亡
Date deathdate;//结构 date 定义的死亡日期
char sex[max_char_num];//性别
int height;//高度
char occupation[max_char_num];//工作职务
char education[max_char_num];//受教育程度
char top_headship[max_char_num];//最高职位
char parentname[max_char_num];//父亲姓名,用于添加节点时用
int Depth;//二叉树深度,输出二叉树时用
char love[max_char_num];//是否已婚
int N;//代数
char lname[max_char_num];//配偶姓名
char lbirthplace[max_char_num];//配偶出生地点
Date lbirthdate;//配偶结构 date 定义的出生日期
char ldead[max_char_num];//配偶是否已死亡
Date ldeathdate;//配偶结构 date 定义的死亡日期
char lsex[max_char_num];//配偶性别
int lheight;//配偶高度
char loccupation[max_char_num];//配偶工作职务
char leducation[max_char_num];//配偶受教育程度
char ltop_headship[max_char_num];//配偶最高职位
};
typedef struct CSNode
{
//个人信息类型结构
Info data;
CSNode *firstchild,*nextsibling,*parent;
//csnode 的第一个孩子节点,下一个兄弟节点,
双亲借点
}*person;
class GEnealogy
{
public:
6
GEnealogy();//构造函数
~GEnealogy();//析构函数
void NewGEnealogy();//建立一个空的家谱树
void CreateGEnealogy();//从磁盘读取文件建立家谱树
void DestroyGEnealogy();//删除家谱树
void SaveGEnealogy();
//保存家谱树
void PreOrderTraverse(fstream &f,person& T ,void (*Visit)(fstream &f,person &T));// 先 序
遍历家谱树,为保存和显示家谱树作准备
void PostOrderTraverse(person& T,void (*Visit)(person& T));//后序遍历家谱树,为删除家
谱树作准备
void ReadNode(fstream &f,person &pnode);
// 从 二 进 制 文 件 读 取 结 点 信 息 以 供
CreateGenealogy()建立家谱树
void FindByRelationship(person pnode);
//按照亲属关系查找某人的父母,孩子,兄
弟,若查找成功则显示出来
int FindByRelationship2(person& T,person& Tname,char* name);
//查询两人的亲属
关系
void Modify(person& pNode,person newValue);//修改某个人的信息
void ModifyData(person &pnode);//修改某人信息时输入 pnode 的个人信息
int CompareDate(Date date1, Date date2);// //比较两日期大小,若 date1 比 date2 早,返回
-1;date1 比 date2 晚,返回 1;date1 与 date2 相等,返回 0
void FindByName(person& T,person& Tname,char* name);//查找姓名=name 的人,若在
家谱中,用 Tname 返回,否则 Tname 为空,Tname 初始为空
void FindByBirthplace(person &T,person& Tname,char* name);//查找出生地=name 的人,
若在家谱中,则显示出来,否则显示出错信息,Tname 初始为空
void FindByBirthday(person &T,person& Tname,Date date);//查找出生日期=date 的人,若
在家谱中,则显示出来,否则显示出错信息,Tname 初始为空
void FindTodayBirthday(person &T);//查找今天生日的人,若在家谱中,则显示祝福信息,
7
否则显示出错信息,Tname 初始为空
void FindByDeathday(person& T,person& Tname,Date date);//查找死亡日期=date 的人,
若在家谱中,则显示出来,否则显示出错信息,Tname 初始为空
void FindBySex(person& T,person& Tname,char* name);//查找性别=name 的人,若在家
谱中,则显示出来,否则显示出错信息,Tname 初始为空
void FindByHeight(person &T,person &Tname,int height);//查找身高=height 的人,若在家
谱中,则显示出来,否则显示出错信息,Tname 初始为空
void FindByEducation(person& T,person& Tname,char* name);//查找受教育程度=name
的人,若在家谱中,则显示出来,否则显示出错信息,Tname 初始为空
void FindByOccupation(person& T,person& Tname,char* name);//查找职位=name 的人,
若在家谱中,则显示出来,否则显示出错信息,Tname 初始为空
void FindByTopHeadship(person& T,person& Tname,char* name);//查找最高职位=name
的人,若在家谱中,则显示出来,否则显示出错信息,Tname 初始为空
void Inquire();//查询函数,调用上述各个 Find 函数按成员基本信息以查询家谱
void InquireNInformation(person& T,person& Tname,int n);//查询第 N 代的所有人员信息
void Delete ( person &rootnode);//删除 rootnode 结点以及他的所有孩子结点
void Display(person info);//显示 info 的各样信息
void Displayl(person info);//显示配偶的各样信息
void Add(person parent, person addNode);//将 addnode 添加到 parent 作为 parent 的孩子结
void DisplayTree(person &pnode);//按照 depth 值在屏幕上输出整个家谱,其中只显示姓
点
名
int IsDateValid(Date date);//检查日期是否符合要求
bool IsLeapYear(int year);//判断某一年是否为闰年
person& GetRoot();//返回家谱的根结点
void InputData(person &pnode);//输入 pnode 的个人信息
8