ImageVerifierCode 换一换
格式:DOCX , 页数:22 ,大小:120.72KB ,
资源ID:8559892      下载积分:12 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/8559892.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(家谱管理方案计划系统设计与实现.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

家谱管理方案计划系统设计与实现.docx

1、家谱管理方案计划系统设计与实现课程设计报告 课程名称 数据结构 课题名称 排序综合 专 业 班 级 学 号 姓 名 联系方式 指导教师 20 11 年 12 月 21 日1. 问题陈述32设计方法阐述32.1总体规划3 2.2功能构想42.2.1增加成员42.2.2修改成员资料52.2.3删除成员62.2.4打开家谱72.2.5新建家谱82.2.6保存家谱102.2.7查看某代信息112.2.8按姓名查找122.2.9按生日查找122.2.10查看成员关系132.2.11按出生日期排序142.3板块整合15 2.4调试分析193总结194. 测试结果201问题陈述家谱用于记录某家族历代家族成员

2、的情况与关系。现编制一个家谱资料管理软件,实现对一个家族所有的资料进行收集整理。支持对家谱的存储、更新、查询、统计等操作。并用计算机永久储存家族数据,方便随时调用。2设计方法阐述2.1总体规划在动手编制程序之前,先要做好程序的规划,包括程序储存数据所用的结构,数据类型等等,只有确定了数据类型和数据结构,才能在此基础上进行各种算法的设计和程序的编写。首先是考虑数据类型。在家谱中,家族成员是最基本的组成部分,对于家族管理中,已经不能再进行细分了,所以选定家族成员作为数据的基本类型,并在程序中定义COperationFamilytree 类。其中COperationFamilytree 类的各种属性

3、可以根据需要进行添加或删除,从日常生活应用的角度出发,制定了COperationFamilytree 类中包含了一下属性:char nameMAX_CHARNUM; /姓名Date birthday; /出生日期In tsex; /性别char addrMAX_CHARNUM; /基本资料int live; /健在否Date deathday; /死亡日期int ChildNums(Person pNode) ; /返回pNode孩子数intInSiblingPos(Person pNode); /返回pNode在其兄弟中的排行为方便计算机进行比较,在familytree类的某些属性中用数字代

4、替了某些不会改变的字符串,譬如性别(1代表男,0代表女)、判断是否健在(1为是,0为否)。在设置日期上,为方便以后的计算与比较,也将日期用整型数字表示19990505表示1999年5月5日,这种表示方法只需在输入和输出上作少许的运算便可方便地与日期进行转换。在家族关系的表示上,并没有用相关家属的姓名作为储存数据,而仅仅是存储了各关系亲属的ID,方便日后作为指针指示调用相对应的家族成员。其中在属性pNode上,其表示的是下一个同父母的弟或妹ID,也就是说,当某家族成员有若干个子女,其pNode仅指向第一个孩子,其余的孩子如何表示呢?可以通过第一个孩子的pNode指示,如此类推,直到孩子的pNod

5、e =0为止。这样就可以避免需在程序设计时预定父母可以拥有的孩子数,有多少孩子就表示多少,实现了动态的储存数据。在选择数据结构方面,从直观来说,选择树型结构通过链表来连接数据无疑是最直观易懂的,我在一开始构思的时候也是从树型结构去想的,但当构思到如何存储和提取数据是,便发现了问题。毫无疑问,用指针来处理数据的确是方便直观,但当我要储存数据是,便发现把指针储存进去是没有作用的,因为当我们下一次读取数据的时候,数据内存地址已经不同了,不在是我们上次存储数据时的地址,也就是说指针这时已经是没有作用了。要解决这样的问题,我们必须要在存储数据之前,先家族树序列化,用数组(或者其他可以用数字表示关系的方法

6、)来存储,并且,再下一次读取数据时,再把数据按照序列号重新组成一个家族树,过程比较繁复,而且实现起来也不容易。所以我便考虑直接用数组来存储数据,即使是在内存中也用数组来处理数据间的联系。运用顺序表这个结构虽然不是那么直观,但在查找数据时的算法设计比较简单容易实现,效率高,而且在内存中的数据可以直接读入到文件中,文件中的数据也可以直接读入内存,不需要进行转换。所以在衡量的各个方面之后,我决定用数组来处理数据间的联系。2.2功能构想构想好总体规划之后,便开始设计程序中需要用到的各个功能函数,初步构想是要先实现最基本的几项功能,其中数据操作的有:增加成员,修改成员资料,删除成员;数据存取的有:打开家

7、谱,新建家谱,保存家谱,另存家谱;数据查询的有:查看某代信息,按姓名查找,按生日查找,查看成员关系,按出生日期排序等等。2.2.1增加成员这项功能做得不够理想,在规划时没有把成员以配偶的形式增加,而只能以子女的形式增加。对应的函数代码如下: void COperationFamilytree:Add(Person parent, Person addNode) /本函数把addNode结点加入到其父结点parent下 addNode-child=addNode-sibling=0; /把欲加入的结点所有指针域置空 addNode-parent=parent; /因addNode欲加为paren

8、t的孩子,故addNode结点的父指针域应指向parent if(parent=0) /若parent为0,则表示欲加addNode为根结点 if(T=0) /若本为空家谱 T=addNode; /把addNode当成根结点 return; addNode-child=T; /使原来的根结点成为新根结点的孩子 T-parent=addNode; T=addNode; return; if(parent-child=0) /parent无孩子,把addNode加入其孩子 parent-child=addNode; else InsertSibling(parent-child,addNode);

9、 /把addNode加到parent孩子的兄弟域中2.2.2修改成员资料修改成员这项功能实现起来比较简单,找到要修改成员的名字,再输入新修改的值,整个函数没有什么需要运用算法的地方,但如果想真正写好这个函数,则需要考虑相当多的细节,譬如各个输入项目的错误处理等等,要非常全面地考虑各项细节。函数代码如下:void CFamilytreeDlg:OnModify() / TODO: Add your command handler code here if(operFamilytree.GetRoot()=0) return; CModifyInfoDlg dlg; HTREEITEM hItem

10、; hItem=m_peTree.GetSelectedItem(); dlg.m_newname=m_peTree.GetItemText(hItem); Person oneself=0; char oldnameMAX_CHARNUM; strcpy(oldname,dlg.m_newname); operFamilytree.Find(operFamilytree.GetRoot(),oneself,oldname); if(dlg.DoModal()=IDCANCEL) return; UpdateData(FALSE); Person newValue=new PersonNode

11、; strcpy(newValue-info.name,dlg.m_newname); /判断家谱中是否已有用户给定的新名字 if(strcmp(newValue-info.name,oldname)=0) ; /用户不修改姓名 else Person p=0; operFamilytree.Find(operFamilytree.GetRoot(),p,newValue-info.name); /查找家谱中有没有此人 if(p!=0) AfxMessageBox(家谱中已有此人!); delete newValue; return; strcpy(newValue-info.addr,dlg

12、.m_newaddr); newValue-info.marry=dlg.m_marry; newValue-info.live=dlg.m_live; newValue-info.birthday.day=dlg.m_birthday_day; newValue-info.birthday.month=dlg.m_birthday_month; newValue-info.birthday.year=dlg.m_birthday_year; if(!newValue-info.live) /如若过世,则应有死亡日期 newValue-info.deathday.day=dlg.m_death

13、day_day; newValue-info.deathday.month=dlg.m_deathday_month; newValue-info.deathday.year=dlg.m_deathday_year; if(!operFamilytree.IsDateValid(newValue-info.deathday) AfxMessageBox(此人信息中死亡日期不合实际!); delete newValue; return; if(operFamilytree.CompareDate(newValue-info.deathday,newValue-info.birthday)=-1)

14、 AfxMessageBox(此人死亡日期不可能比其出生日期早!); return; operFamilytree.Modify(oneself,newValue); RefreshTree(); RefreshList(); IsFamilytreeModified=true; /置家谱修改标记为真 delete newValue;2.2.3删除成员用数组来储存数据,最麻烦的就是删除数组元素了,在这个程序中,删除数组不但意味着要重新排列各成员,还要重新更新各成员的关系,所以我个人认为在这个程序中,删除成员函数可以说是一个难点。通过分析,发现删除成员的情况就只有两种,只要针对这两种情况处理好删

15、除,就可以完成成员删除这个功能。,删除的成员是出于家族中最底层的,也就是删除该成员不会牵连其他成员,但这也需要处理好其父母的孩子数。,删除的成员还有子孙,则需要连带所有子孙都要删除出家谱。遇到这种情况,不但要像上一种情况那样处理父母和兄弟姐妹的关系,还要记录牵连删除的总人数,因为删除不再是简单删除了一个人,而是若干个,通过递归调用,可以统计出需要删除的数目 删除函数的相关代码如下:void COperationFamilytree:Delete(Person &rootNode) /本函数删除以rootNode为根结点的所有结点 if(rootNode-parent) /如果rootNode有

16、父结点 if(rootNode-parent-child=rootNode) /如果rootNode为其父结点的第一个孩子 rootNode-parent-child=rootNode-sibling; /因要删掉rootNode,故把其父结点的孩子指针指向rootNode的第一个兄弟 else /如果rootNode不是父结点的第一个孩子 Person p=rootNode-parent-child; /找到rootNode应在兄弟中的位置 for(;p-sibling!=rootNode;p=p-sibling) ; p-sibling=rootNode-sibling; /插入到兄弟中

17、PostOrderTraverse(rootNode-child,DestroyNode);/删除以rootNode-child为根结点的所有结点 if(rootNode=T) /删除rootNode结点。如果rootNode为根结点,则删除根结点T DestroyNode(T); else DestroyNode(rootNode);2.2.4打开家谱打开家谱函数的相关代码如下:int COperationFamilytree:ReadNode(FILE *fp, Person &T,char* parentname)/本函数从文件fp中读取信息到结点T中,并读取结点的父亲名字到字符数组pa

18、rentname中/分别读取结点值,为:姓名,出生日期(年,月,日),婚否,地址,健在否,(如过世,还有死亡日期) fscanf(fp,%s%d%d%d%d%s%d,T-info.name,&T-info.birthday.year,&T-info.birthday.month, &T-info.birthday.day,&T-info.marry,T-info.addr,&T-info.live); if(T-info.live=0) fscanf(fp,%d%d%d,&T-info.deathday.year,&T-info.deathday.month, &T-info.deathday

19、.day); fscanf(fp,%s,parentname); if(!IsDateValid(T-info.birthday) /出生日期合法性检查 return FILE_DATA_NOT_PRACTICAL; if(T-info.live=0) /若过世,死亡日期合法性检查 if (CompareDate(T-info.birthday,T-info.deathday)!=-1) return FILE_DATA_NOT_PRACTICAL; if(!IsDateValid(T-info.deathday) return FILE_DATA_NOT_PRACTICAL; return

20、OK;2.2.5新建家谱新建家谱函数的相关代码如下:void COperationFamilytree:NewFamilytree() /本函数新建一空家谱 DestroyFamilytree(); /删除原有家谱 T=0;int COperationFamilytree:CreateFamilytree(CString filename) /本函数建立一新家谱 DestroyFamilytree(); /建立一新家谱之前,清空原有家谱 FILE* fp; if(fp=fopen(filename,r)=0) /打开文件filename return READ_FILE_ERROR; T=ne

21、w PersonNode; /定义根结点 if(!T) return NOT_ENOUGH_MEMORY; T-child=0; T-sibling=0; T-parent=0; Person parentT, temp; /定义两个临时结点 char parentnameMAX_CHARNUM; /定义一个临时字符串数组 /读取根结点值,(姓名,出生日期(年,月,日),婚否,地址,健在否,(如过世,还有死亡日期) int result; result=ReadNode(fp,T,parentname); if(result=FILE_DATA_NOT_PRACTICAL) delete T;

22、 /若不合法,删除申请的堆空间 T=0; return result; if(strcmp(T-info.name,parentname)=0) /根结点名字与其父亲名字相同,说明为空树 delete T; T=0; return PEDIGREE_EMPTY; temp=new PersonNode; /申请一结点 if(!temp) /申请失败 DestroyFamilytree(); /释放申请空间 return NOT_ENOUGH_MEMORY; result=ReadNode(fp,temp,parentname); while(strcmp(temp-info.name,pare

23、ntname)&strcmp(temp-info.name,end) /读取信息结束的条件是两个人的名字同为end if(result=FILE_DATA_NOT_PRACTICAL) /若数据不合法,释放已申请空间,然后返回 delete temp; DestroyFamilytree(); return result; parentT=0; Find(T,parentT,parentname); /找到parentname所在结点parentT if(parentT) /如果parentT存在,说明parentname在家谱中 /并且parentname为temp的父亲 int cmp;

24、cmp=CompareDate(temp-info.birthday,parentT-info.birthday); if(cmpchild=temp-sibling=0; temp-parent=parentT; /temp的父指针指向parentT; if(parentT-child) /parentname已经有孩子 InsertSibling(parentT-child,temp); /if else /parentname无孩子,则temp应为 parentT-child=temp; /parentname的第一个孩子 /if else /parentT不存在,说明家谱中不存在par

25、entname此人 DestroyFamilytree(); /返回出错信息 return FILE_DATA_ERROR; temp=new PersonNode; /申请一结点 if(!temp) /申请失败 DestroyFamilytree(); /释放申请空间 return NOT_ENOUGH_MEMORY; result=ReadNode(fp,temp,parentname); /继续读取数据 /while if(temp) delete temp; fclose(fp); return OK;2.2.6保存家谱保存家谱函数的相关代码如下:int COperationFamil

26、ytree:SaveFamilytree(CString filename) /本函数保存家谱到文件filename中 FILE* fp; if(fp=fopen(filename,w)=0) /打开文件filename return WRITE_FILE_ERROR; PreOrderTraverse(fp,T,SaveNode); /从根结点开始存储家谱数据 /置家谱数据结束标记(一结点的名字与其父结点的名字同为end) fprintf(fp,%s %d %d %d %d %s %d %s,end,1999,12, 2,1,end,1,end); fclose(fp); return OK

27、;void COperationFamilytree:PreOrderTraverse(FILE* fp,Person &T, void (_cdecl *Visit)(FILE* fp,Person &) /本函数把所有以T结点为根结点的结点值存到文件fp中 if(T) (*Visit)(fp,T); PreOrderTraverse(fp,T-child,Visit); PreOrderTraverse(fp,T-sibling,Visit); void SaveNode(FILE *fp, Person &pNode) /本函数向文件fp中存取一结点pNode char ch=n; if(pNode) fprintf(fp,%s %d %d

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1