数据结构课程设计之简易家谱 报告_第1页
数据结构课程设计之简易家谱 报告_第2页
数据结构课程设计之简易家谱 报告_第3页
数据结构课程设计之简易家谱 报告_第4页
数据结构课程设计之简易家谱 报告_第5页
已阅读5页,还剩23页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、编 号: 学 号: 202140410119 课 程 设 计教 学 院计算机学院课程名称数据结构课程设计题 目简易家谱系统专 业计算机科学与技术班 级1班姓 名陈建辉同组人员周海涛,石义沣,明廷柱指导教师程细才2021年1月8日 目 录一 概述222二 总体方案设计332简单家谱系统的主要特点及功能5三 详细设计71. 查询全部的家谱成员信息773.在家谱中添加新成员,并追加到文件中9四 程序的调试与运行结果说明121.实验结果截图:122调试时遇到的问题12五 课程设计总结13附录一:程序源代码16附录二:参考文献25一 概述1.课程设计的目的1理解和掌握该课程中的有关根本概念,程序设计思想

2、和方法。2培养综合运用所学知识独立完成课题的能力。3培养勇于探索、严谨推理、实事求是、有错必改,用实践来检验理论,全方位考虑问题等科学技术人员应具有的素质。4掌握从资料文献、科学实验中获得知识的能力,提高学生从别人经验中找到解决问题的新途径的悟性,初步培养工程意识和创新能力。2.课程设计的要求设计要求:输入家族成员情况,建立树结构,统计家族成员人数,能查询家族成员辈份情况。系统功能:1. 输入、修改与删除家谱信息功能2. 查询功能:1某家谱成员的所有子孙的集合2某家谱成员的所有祖先的集合3某家谱成员的所有同辈成员的集合4求某家谱成员的所有上一辈成员的集合5给出两个家谱成员,确定他们的关系二 总

3、体方案设计 此次课程设计的整体思路是采用遍历算法,整个树的定义使用两个结构体来表示,一个结构体专门用于存放每一个节点的信息,另一个节点中定义了三个指针域,分别为父指针域兄长指针域,兄弟指针域,子指针域,整个树的输入采用文件导入的方式,首先将文件导入到树中,文件包含每一个家族成员的信息,以及一个标志位flag,标志位的值为0,1,2,0表示此节点没有兄弟节点,1表示此节点至少有一个子节点,2表示此节点至少有一个兄弟节点,使用的算法是先定义一个链式队列,将文件中第一个节点的内容读取放入开辟的树的节点的空间里,然后将树节点放入队列中,此时队列不为空,以队列不为空为判断条件,进行while循环,判断f

4、lag的值,循环体中再进行文件读取,循环中进行判断,假设flag为0,那么继续判断队列是否为空,为空就结束循环,假设不为空,那么继续出队列,取标志位进行判断,假设标志位为1,那么生成新节点,用刚刚出列的节点的子指针域进行指向新节点,新节点的父指针域指向出列的节点,剩余的域为空,然后将新生成的节点插入到队列中,假设flag为1,那么进行兄弟节点的插入,继续循环,假设flag不为0,那么在进行判断,假设为2,那么继续进行兄弟节点的插入,以此类推,根据文件的读取,将树生成,本程序中所有的算法都基于以上所介绍的算法。关于IO的设计:考虑到题目要求家谱信息以树形的形式一次读入内存,而个人的各种资料 现在

5、虽然条目不多,但随着程序的升级,以后可能变得越来越大。我把树形结构和个人信息记录的文档分为两个文件保存在外存中,一个文件串行化地记录家谱树的结构信息,保存少量个人信息作为识别标志;另一个文件保存完整的个人信息,所有的个人信息以线性记录的方式记录在其中。当程序运行要读入家谱结构时,只读入保存少量记录的文件并建立起树形结构。索引时,以树形中的少量信息为依据在另一个文件中找到全部的各人信息资料。这样的好处主要有两点:1. 由于树形结构是串行化记录于外存,一个节点记录屡次,信息大量冗余,如果树形节点中保存全部信息,必将造成大量的空间浪费;只保存作为索引的少量信息在树形结构中,节约了空间。2. 由于结构

6、的精简,在家谱初始化时读入内存需要的时间相应减少,节约了装载时间。这样做存在的问题:每次执行修改,添加,删除,查询时都要直接访问外存来取得或写入数据。内外存访问上的巨大时间差的存在,使得进行这些操作相对来说并不显得很高效。关于树形的结构:在树形结构的选择上,根据实际中多子女的现象选择一般树,考虑到家谱中成员可能存在的不定成员数问题,抛弃了以数组为根底的一般树方案,决定用链表来实现。树形结构的外存保存。为了提高效率,树形结构在程序初始化时由外存文件一次读入内存,此后不管插入还是修改,删除都不再对外存的树结构保存文件进行操作,只在内存中处理,程序退出时对外存树结构文件进行一次更新。也就是说,不管在

7、程序运行中中对家谱结构进行多少种,多少次的操作,外存的树结构文件始终只会被程序访问两次。关于功能的设计以根本要求为准:1. 插入:用户按提示输入资料,在树形中插入节点,在个人完整记录文件中添加插入人的所有信息并保存。2. 删除:用户输入被删除人的识别关键字即名字,在树形结构中删除此人,在个人记录文件中不处理。3. 修改:用户输入被删除任的识别关键字即名字,从个人完整记录文件中读出该人所有信息供用户进行修改,然后写回。4. 查询:完成了关键字查找局部和关系查找局部,读出个人全部信息并显示。关于个人资料单元:由于树形节点要涉及到大量的逻辑操作,要用到一些运算符的重载,个人资料在树形单元定义为cla

8、ss类,而写入文件的单元存萃是数据,定义为简单的struct类。定义为struct和class类,使得不管是树形结构插入删除还是IO都是对整个结构体进行的操作,这样,如果要往结构体中添加假设干个新信息条目或取消一些不必要的信息条目,需要修改的代码非常之少,方便了以后的升级。2简单家谱系统的主要特点及功能 本系统的主要特点是算法简洁,易于阅读,用户交互界面良好。 主要实现功能: 1.确定指定成员在家族中的辈份 2.输出指定辈的所有成员 3.在家谱中添加新成员,并追加到文件中 4.输出指定家庭的所有成员 5.退出本系统 各功能的算法思想: 1.读出家谱并显示 存储结构用栈,按照先显示双亲,然后显示

9、其所有孩子的顺序显示所有家 庭成员。 2.确定指定成员在家族中的辈份用求成员所在的二叉树中的层数按层遍历二叉树来确定,这里采用的是递归算法 3 .输出指定辈的所有成员此处定义了一个新的结构体类型增加存储节点所在的层数,定义如下: typedef struct cc struct cc *child, *next;/next指向同辈份的人物 char Name; JPNode; 并用一个队列来比拟显示同辈分的所有成员。 4.在家谱中添加新成员,并追加到文件中首先,输入一个新成员的名字;然后,输入其双亲;之后,再添加到整个存储二叉链表中。然后,再将新的存储结构写回到文件中。二叉链表的结点类型为:t

10、ypedef struct node ElemType data10; /存放成员的名字 struct node *child; /其孩子指针 struct node *brother; /其兄弟指针 BTNode; 5.输出指定家庭的所有成员首先,设一个栈,并设一个标记位,先置1;然后,找到输入的要待显示的成员,将标记位置0;再次,显示其孩子和兄弟,依次下去直到显示完其所有的亲戚。 6.退出本系统。 三 详细设计1. 查询全部的家谱成员信息该局部程序所完成的具体功能是把贾府中所有的成员信息按照辈份依次显示出来,首先从贾无名开始,然后下一辈份的贾演、贾源等最后是贾蓉的信息。每个辈份之间都有间隔

11、。设计思想:利用队列遍历所有的结点,在遍历的同时依次输出每个结点的值即为家庭成员的信息。代码如下:void Dis_Family(JPNode *p)char nameNAME_length;JPNode *here = NULL;printf("请输入该家庭的首个成员:"); scanf("%s",name);here = Find_Name(p,name);if(here = NULL) printf("无该家庭!n"); return;printf("n");DispJP(here);2.确定指定成员在家族中

12、的辈份该局部代码所完成的具体功能是查询某一个家庭成员的信息。首先手动输入你要查询的那个成员的名字,然后利用队列依次查找该成员的信息,如果该家谱中存在这个成员那么输出该成员的有关信息;如果家谱中不存在该成员,那么输出提示信息:家谱中无此人!设计思想:有两个函数,一个函数queryElemt利用队列遍历所有的节点,在遍历的同时即可查找所输入的成员是否存在该家谱中;另外一个函数queryElemtoFamily用于输入你要查找的成员的辈分和输出查找的结果。代码如下:int beifen(JPNode *p, char name) static int n = 1;static int level =

13、 0;if(p = NULL )return level;if(Equal(p->Name,name) = 1)return (level=n);n+; beifen(p->child,name);n-; beifen(p->next,name); /向右查询n不必加(先加后减)!return level;void Bei_Fen(JPNode *p) char nameNAME_length;int n=0;printf("请输入要查明辈分的人的姓名:"); scanf("%s",name); n = beifen(p,name);i

14、f(n = 0)printf("该家族中无此人!n");else printf("n %s 是 %s 家族中的第%d辈 n",name,p->Name,n);/ /*输出指定辈的所有成员*/void chabei(JPNode *p, int bei) static int n = 1;static int tag = 0;if(p = NULL )return;if(bei = n)tag = 1;printf("%s ",p->Name);n+; chabei(p->child,bei);n-; chabei(p

15、->next,bei); /向右查询n不必加(先加后减)!if(tag = 0)printf("该家族中还没有这一辈呢.n");void Disp_Pei(JPNode *p)int bei; printf("n你想要查看那一辈的成员:");scanf("%d",&bei); printf("n.该家族中辈分为%d的成员有.nn",bei);chabei(p,bei);printf("n");3.在家谱中添加新成员,并追加到文件中该局部程序所完成的具体功能是:输入你要添加人的名字,

16、比方吴长,他会被添加到文件中。设计思想:首先确定他的父母,然后把他添加到他的父母的根节点之下。代码如下:int Equal(char const *p,char const q)/判断两个字符串是否相等while(*p+ = *q+)if(*p = '0' && *q = '0')return (1);return (0);JPNode *Find_Name(JPNode *s, char *parent)/定位家谱中的成员。返回其指针(地址)static JPNode *here = NULL;if(s = NULL)return here;i

17、f(Equal(s->Name,parent) = 1)return (here=s); here = Find_Name(s->child,parent);here = Find_Name(s->next,parent);return here;void Print_FILE(JPNode *p,FILE *fp) static int level=0; int i; if(p != NULL) for(i=0;i<level;fprintf(fp,"t"),i+); fprintf(fp,"%sn",p->Name);

18、else return; level+; Print_FILE(p->child,fp); level-; Print_FILE(p->next,fp); void ADD_number(JPNode *p) /在家谱中添加新成员,并写入文件char parentNAME_length,nameNAME_length;FILE *fp = NULL;JPNode *here = NULL; JPNode *s = (JPNode *)malloc( 2*sizeof(void *) + strlen(name) + 1 );s->next = s->child = NU

19、LL; printf("请输入要添加的新成员的双亲姓名:");scanf("%s",parent); printf("请输入要添加的新成员的姓名:"); scanf("%s",name); strcpy(s->Name,name);here = Find_Name(p,parent);if(here->child = NULL)here->child = s;else here = here->child;while(here->next != NULL)here = here->

20、;next;here->next = s; fp = fopen("jiapu_data.txt","w");Print_FILE(p,fp);fclose(fp);四 程序的调试与运行结果说明1.实验结果截图:2调试时遇到的问题1中选择功能3添加新成员,添加完成员后,写回文件时出现了错误,原本添加的为其中一个结点的孩子,结果写回文件时却成了该结点的孙子,也就是本是要添加为此结点的孩子的兄弟,结果却成了其孩子的孩子。最后经过单步跟踪发现写入文件的函数编写错误,缺少判断条件,经过修改后,此问题得到了解决。2中选择功能0显示此家谱,没有按照事先存储在t

21、xt中的文件显示,通过认真检查程序中显示成员的函数按层遍历、不断调试,发现并不是该显示函数的错误,因为在功能4输出指定家庭的所有成员中,也是通过调用该函数实现输出功能,于是,检查txt文件中事先存储的家谱成员,发现是由于“与“没有匹配好,导致没有按照预期想法创立二叉树造成的错误,通过修改txt文件,使得错误得以解决。五 课程设计总结本组的课程任务为简易家谱,在组长的带着下,经过我们共同的努力,本程序主要任务已根本完成。另外本程序功能完善,具有良好的交互性,可以满足用户多种需求。美中缺乏的是本系统没有采用之前我所设想的递归,我们这次改用队列来建树、查找等,这样代码少有一定的冗余,如果时间充分,我

22、们会更加精进,争取采用代码量相对较少的递归算法,另外更加完善一些功能,包括增加日志功能,给不同级别的人员设置管理权限,比方说家谱管理员在获得本族长的同意下修改某个成员的信息,而其他成员只具备浏览的功能。在本程序简易家谱的创作过程中,我们组员各抒己见,意见很不一致,包括是采用递归算法,还是采用队列;数据成员的结构如何定义,甚至包括成员的名字,大家的意见都不统一。但是在探讨的过程中,我们也理解自己思维的缺乏,也见识了另外不同的编程思想,这也让我体会到了交流的魅力。只有我们大家坦诚布公的把自己想法说出来,然后我们在理性的分析算法的优劣与可行性,这样就能结合大家共同的智慧,组织大家共同的力量,为共同的

23、目标努力奋斗。次大作业的推荐学时是20个学时,但回想起来,光这篇报告就写了有三四个学时,由于我本身编程能力不强,构思,写代码,修改,调试的时间加起来绝对是有余了。虽然付出不少,但在前期的设计组织中,中期的代码编写和后期的修改调试中都学到了不少。起初我的家谱中数据并不是以结构的形式打包,读入,写出操作的时候代码异常繁琐,这是恰好看到一本编程书上有用结构输入输出的范例,深深感到结构化确实是方便多了,而且读写不容易出错。当时我的代码本已经写了有一大半,思虑再三,还是决定进行大换血,家谱数据的改变让程序来了个大换血,提高了效率的同时,代码几乎变了一多半。我想,当时唯一不变的就只有对整个程序越来越清醒地

24、认识和对将要完成的代码的更准确地把握。代码完成之后,一编译,至少有五六十个错误提示,而且最末debug一行栏的最后一行提示因为先前的错误而中断编译,有的是指针指向错误,有的是忘了分号,括号,更多的是一些赋值错误,前面的错误都好改,赋值错误就比拟麻烦了,因为要使用赋值的地方太多,逐行改代码虽然也可以,但实在是一个巨大工程。所谓巧招必是险招至少对我是这样说来惭愧,我只有尝试着写以前从未是过的运算符重载函数,为此专门翻找出了大一的C+教材,温习了一番运算符重载才战战兢兢地搞定了赋值问题。试运行时出现了更多的问题,而且更具有隐蔽性,几次为了一个逻辑错误而来回反复地翻看代码,嘿嘿,再加上网上有代码的传说

25、,让人有想直接放弃的冲动,但一想到写代码的辛苦,觉得放弃了实在可惜。那几天吃饭,睡觉,走路,看电视,随时随地,都记挂着那几处错误,脑子都会浮现出代码的影子,不自觉地开始找错误。哎那种感觉!不知道是充实还是急迫地烦恼,最后终于找到了,根本来不急快乐,只有松了一口气和一阵的疲惫。现在程序终于完成了,心里的石头也下了地。至于成绩,不想了 另外,通过本次课程设计,我觉得自己最大的收获就是:1学会了怎样将课堂所学知识运用到较为实际的应用中来由于对二叉链表的存储比拟感兴趣,我选做的是家谱,开始觉得无从下手,但是经过仔细分析后,渐渐找到一点思路首先创立,然后分别实现各个功能,最后利用菜单实现选择功能并输出结

26、果。2锻炼提出问题、解决问题和自学的能力 家谱的实现要求读、写文件,于是“如何将文件从文档中读出,“ 怎么写入文件都是要满足要求必须解决的问题。为此,我查找了很多资料,最后自学、解决这一问题3增加了对编程的热爱和兴趣本次编写家谱程序的过程中,我体验到了编程的酸甜苦辣:开始,由于即将运用所学知识设计实际问题而冲动兴奋;后来编写程序过程中,在想不出算法如何实现或者追求空间、时间上高效率的算法时会比拟纠结;以及遇到BUG时,追踪数据的痛苦;当然,还有解决问题后的冲动与自豪。所有这些都增强了我对编程的热爱、提高了我对计算机专业的兴趣。附录一:程序源代码#include<stdio.h>#i

27、nclude<stdlib.h>#include<string.h>#include<ctype.h>#define NAME_length 50 /名字最大长度#define LINE_length 100 /文本行最大长度typedef struct cc struct cc *child, *next;/next指向同辈份的人物 char Name;JPNode;void clear(char p,int n) /清空字符数组pwhile(n- > 0)*p+ = '0' static JPNode *last = NULL; s

28、tatic int last_level = 0;void AddJP(JPNode *head, char const name, int level) JPNode *s = head, *r = NULL; JPNode *p = (JPNode *)malloc( 2*sizeof(void *) + strlen(name) + 1 ); p->child = p->next = NULL; strcpy(p->Name,name); if( *s = NULL) *s = p; last = p; return; if(level - last_level = 1

29、) last->child = p; last = p;last_level = level;return; if(level = last_level) && (*s != NULL)last->next = p; last = p;last_level = level;return; r = *s; /r指向家谱树 last_level = level; while( level- > 0) /找到相同的辈分 while(r->next != NULL)r = r->next; r = r->child; /以兄弟连接 while( r-

30、>next != NULL) r = r->next; r->next = p; last = p;void CreatJP(JPNode *head) char nameNAME_length="", lineLINE_length="" char *p = NULL; int level=0,i=0; /辈分,以制表符个数表示 FILE *fp = NULL; fp = fopen("jiapu_data.txt","r"); if(fp = NULL) printf("open e

31、rror!n"); exit(1); while(level=0, i=0, fgets(line,LINE_length,fp) != NULL) p = line; while(*p+ = 't')level+; /计算辈分 ,计算完后p指向名字开始处 while(linei+ != 'n') ; linei-1='0' /读入的换行符用字符串结束标识符替换 strcpy(name,p-1); AddJP(head,name,level); clear(name,NAME_length); clear(line,LINE_lengt

32、h); fclose(fp);void DispJP(JPNode *p)/从p指向的结点显示该家族 static int level=0; int i; if(p != NULL) for(i=0;i<level;printf("t"),i+); printf("%sn",p->Name); else return; level+; DispJP(p->child); level-; DispJP(p->next); / /*在家谱中添加新成员,并追加到文件中*/int Equal(char const *p,char cons

33、t q)/判断两个字符串是否相等while(*p+ = *q+)if(*p = '0' && *q = '0')return (1);return (0);JPNode *Find_Name(JPNode *s, char *parent)/定位家谱中的成员。返回其指针(地址)static JPNode *here = NULL;if(s = NULL)return here;if(Equal(s->Name,parent) = 1)return (here=s); here = Find_Name(s->child,parent);

34、here = Find_Name(s->next,parent);return here;void Print_FILE(JPNode *p,FILE *fp) static int level=0; int i; if(p != NULL) for(i=0;i<level;fprintf(fp,"t"),i+); fprintf(fp,"%sn",p->Name); else return; level+; Print_FILE(p->child,fp); level-; Print_FILE(p->next,fp); v

35、oid ADD_number(JPNode *p) /在家谱中添加新成员,并写入文件char parentNAME_length,nameNAME_length;FILE *fp = NULL;JPNode *here = NULL; JPNode *s = (JPNode *)malloc( 2*sizeof(void *) + strlen(name) + 1 );s->next = s->child = NULL; printf("请输入要添加的新成员的双亲姓名:");scanf("%s",parent); printf("请

36、输入要添加的新成员的姓名:"); scanf("%s",name); strcpy(s->Name,name);here = Find_Name(p,parent);if(here->child = NULL)here->child = s;else here = here->child;while(here->next != NULL)here = here->next;here->next = s; fp = fopen("jiapu_data.txt","w");Print_F

37、ILE(p,fp);fclose(fp);/ /*输出指定家庭的所有成员*/void Dis_Family(JPNode *p)char nameNAME_length;JPNode *here = NULL;printf("请输入该家庭的首个成员:"); scanf("%s",name);here = Find_Name(p,name);if(here = NULL) printf("无该家庭!n"); return;printf("n");DispJP(here);/ /*确定指定成员在家族中的辈份*/int

38、beifen(JPNode *p, char name) static int n = 1;static int level = 0;if(p = NULL )return level;if(Equal(p->Name,name) = 1)return (level=n);n+; beifen(p->child,name);n-; beifen(p->next,name); /向右查询n不必加(先加后减)!return level;void Bei_Fen(JPNode *p) char nameNAME_length;int n=0;printf("请输入要查明辈

39、分的人的姓名:"); scanf("%s",name); n = beifen(p,name);if(n = 0)printf("该家族中无此人!n");else printf("n %s 是 %s 家族中的第%d辈 n",name,p->Name,n);/ /*输出指定辈的所有成员*/void chabei(JPNode *p, int bei) static int n = 1;static int tag = 0;if(p = NULL )return;if(bei = n)tag = 1;printf("%s ",p->Name);n+; chabei(p->child,bei);n-; chabei(p->next,bei); /向右查询n不必加(先加后减)!if(tag = 0)printf("该家族中还没有这一辈呢.n");void Disp_Pei(JPNode *p)int bei; printf("

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论