1、家族关系查询系统11 课程设计介绍 1.1 课程设计项目简介 家谱是一种以表谱形式,记载一个以血缘关系为主体的家族世系繁衍和重要人物事迹的特殊图书载体。家谱是中国特有的文化遗产,是中华民族的三大文献之一,属珍贵的人文资料,对于历史学,民俗学,人口学,社会学和经济学的深入研究,均有不可替代的重要功能。本项目对家谱管理进行简单的模拟,以实现查看祖先和子孙个人信息 、插入家族成员等功能。 1.2 课设题目分析本程序的实质是完成对家谱成员信息的建立、查找、插入等功能。可以首先定义家族成员的数据结构,然后将每个功能写成一个函数来完成对数据的操作,最后完成主函数以验证各个函数功能并得出运行结果。本程序包含
2、以下几个模块(1)建立家族关系树。此模块将构建一个家族关系,对数据初始化,构造关系树并录入数据一遍后续程序使用。(2)添加新成员。此模块将添加一个新成员,实现对家族关系的修改。(3)家族关系的查询。此模块将实现对家族不同关系的查询(4)主程序模块。此模块实现整个程序的进入和进出,以及各种初始化处理。(5)1.3 课程题目原理与数据结构因为家族的成员之间存在一个对多个的层次结构关系,所以不能用线性表来表示和实现。家谱从形状上看像一颗倒长的树,所以用树结构来表示比较合适。树形结构是一类非常重要的非线性数据结构,直观看来树是以分支关系定义的层次结构。因此本课程设计可以采用的数据结构有树状结构和队列。
3、树状结构采用三叉链表来实现,队列采用链式队列实现。家族关系查询系统21.4 功能分析说明图家族关系查询系统建立一个家族关系 打开一个家族关系 添加一个家庭成员按关系查找各个家庭成员退出系统查找一个成员的祖先 查找成员的子孙后代查找成员祖先路径 查找成员是第几代 查找一个成员双亲 查找一个成员的兄弟 查找成员的堂兄弟 查找一个成员的孩子家族关系查询系统32 分析与实现 2.1 基本数据结构和栈队的操作2.1.1 结点基本数据结构和链队的定义/*家族关系树实现*/#include #include #include#include#include#include#include#include#d
4、efine TRUE 1#define FALSE 0#define OK 1#define ERROR -1#define INFEASIBLE -1typedef char DataType;#define MAXNUM 20typedef struct TriTNode/* 树的三叉链表存储结构*/ DataType dataMAXNUM;struct TriTNode *parent;/* 双亲*/struct TriTNode *lchild;/* 左孩子*/struct TriTNode *rchild;/* 右孩子*/TriTree;typedef struct Node/* 队
5、列的结点结构*/ TriTree *info;struct Node *next;Node;typedef struct/* 链接队列类型定义*/struct Node *front; /* 头指针*/家族关系查询系统4struct Node *rear; /* 尾指针*/LinkQueue;DataType fnameMAXNUM,family50MAXNUM;/* 全局变量*/2.1.2 链队的基本操作LinkQueue *LQueueCreateEmpty( )/* 建立一个空队列*/ LinkQueue *plqu=(LinkQueue *)malloc(sizeof(LinkQueu
6、e);if (plqu!=NULL)plqu-front=plqu-rear=NULL;elseprintf(“内存不足!n“);return NULL;return plqu;int LQueueIsEmpty(LinkQueue *plqu)/* 判断链接表示队列是否为空队列*/ return(plqu-front=NULL);void LQueueEnQueue(LinkQueue *plqu,TriTree *x)/* 进队列*/ Node *p=(Node *)malloc(sizeof(Node);if(p=NULL)printf(“内存分配失败!n“);else p-info=x
7、;p-next=NULL;if(plqu-front=NULL)/* 原来为空队*/plqu-front=p;elseplqu-rear-next=p;plqu-rear=p;家族关系查询系统5int LQueueDeQueue(LinkQueue *plqu,TriTree *x)/* 出队列*/ Node *p;if(plqu-front=NULL)printf(“队列空!n“);return ERROR;else p=plqu-front;x=p-info;plqu-front=plqu-front-next;free(p);return OK;TriTree *LQueueGetFro
8、nt(LinkQueue *plqu)/* 在非空队列中求队头元素*/ return(plqu-front-info);2.2 建立家族关系2.2.1 建立家族关系并存入文件 基本思想:首先输入家族关系的名称,以此名称为文件名,建立文本文件接下来按层次输入结点信息,输入一个在文件中写入一行同时将输入的信息保存 到二位字符数组 family 中。字符数组 family 是全局变量,存储临时信息 . 注意,输入时每个结点信息占一行,一个结点有多家族关系查询系统6个兄弟,以“”作为兄弟结束标志,结点若无孩子,直接以“”代替。依次输入各节点信息,以“#” 作为结束标志。最后使用函数 CreateTri
9、Tree 建立家族关系树.TriTree *Create(DataType familynameMAXNUM)/* 建立家族关系并存入文件*/int i=0; /* i控制family 数组下标*/DataType ch,strMAXNUM; /* ch存储输入的y或n,str存储输入的字符串*/TriTree *t;FILE *fp;strcpy(fname,familyname); /* 以家族名为文本文件名存储*/strcat(fname,“.txt“);fp=fopen(fname,“r“); /* 以读取方式打开文件*/ if(fp) /* 文件已存在*/fclose(fp);pri
10、ntf(“%s 的家族关系已存在!重新建立请按“Y”,直接打开请按“N”n“ ,familyname);ch=getchar();getchar(); /* 接收回车*/if(ch=N|ch=n)lixianliguoyu liguojun liguoqiangliyongzhi liyongrui liyongmingliwende liwenjia 家族关系查询系统7t=Open(familyname);/* 直接打开*/return t;if(!fp|ch=Y|ch=y) /* 重新建立,执行以下操作*/fp=fopen(fname,“w“); /* 以写入方式打开文件,不存在则新建*/
11、printf(“请按层次输入结点,每个结点信息占一行 n“);printf(“兄弟输入结束以“”为标志,结束标志为 “#”n. “);gets(str);fputs(str,fp);fputc(n,fp);strcpy(familyi,str); /* 将成员信息存储到字符数组中*/i+; /* family数组下标后移*/while(str0!=#) printf(“. “); /* 以点提示符提示继续输入*/gets(str);fputs(str,fp); /* 写到文件中,每个信息占一行*/fputc(n,fp);strcpy(familyi,str);/* 将成员信息存储到字符数组中*
12、/i+; /* family数组下标后移*/fclose(fp); /* 关闭文件*/t=TriTreeCreate(); /* 根据family数组信息创建三叉树*/printf(“家族关系已成功建立!n“);return t; /* 返回树*/2.2.2 建立家族关系树家族关系查询系统8基本思想:采用指针数组作为指针,保存输入的结点地址。队列的尾指针指向当前结点。头指针指向当前结点的双亲结点。输入的结点信息已存储在字符数组 family 中。将信息复制到字符串数组“ch”中 ,如果“ch“不是“” ,则建立一个新结点。若新结点是第一个结点,则它是根结点,将其入队,指针 tree 指向这个根
13、节点;如果不是根结点,则将当前结点链接到双亲结点上,即当前结点的双亲指针就是队头元素,然后将当前结点入队列。接着判断 flag 的值,如果 flag=0,表示当前结点没有左孩子,那么当前结点就是双亲的左孩子。否则,当前结点就是双亲的右孩子。用指针 root 指向刚刚入队的结点。继续复制数组 family 的下个元素。如果“ch” 是。则flag=0(因为“”后面的第一个孩子为左孩子) ,同时判断“”是否是第一次出现,如果是第一次,则令标志 star=1;如果不是第一次出现。则出队列,root 指向队头元素(实际上root 总是指向双亲结点) 。继续复制 family 的下一个元素。知道遇到“#
14、”结束。函数返回指针 tree。/* 建立家族关系树*/TriTree *TriTreeCreate()TriTree *t,*x=NULL,*tree,*root=NULL;LinkQueue *q=LQueueCreateEmpty();/* 建立一个空的队列,存储指向树的指针*/int i=0,flag=0,start=0;DataType strMAXNUM; /* 存放family数组中信息*/strcpy(str,familyi); /* 复制*/i+; /* family数组下标后移*/while(str0!=#) /* 没遇到结束标志继续循环 */while(str0!=) /
15、* 没遇到兄弟输入结束标志继续*/if(root=NULL) /* 空树*/root=(TriTree *)malloc(sizeof(TriTree);/* 申请空间*/strcpy(root-data,str);家族关系查询系统9root-parent=NULL;root-lchild=NULL;root-rchild=NULL;LQueueEnQueue(q,root); /* 将root存入队列*/tree=root;else /* 不为空树*/t=(TriTree *)malloc(sizeof(TriTree); /* 申请空间*/strcpy(t-data,str);t-lchi
16、ld=NULL;t-rchild=NULL;t-parent=LQueueGetFront(q); /* 当前结点的双亲为队头元素*/LQueueEnQueue(q,t); /* 入队*/if(!flag) /* flag为,当前结点没有左孩子*/root-lchild=t;else /* flag为,当前结点已有左孩子*/root-rchild=t;root=t; /* root指向新的结点t */flag=1; /* 标记当前结点已有左孩子 */strcpy(str,familyi); i+;if(start!=0) /* 标记不是第一次出现 “”*/LQueueDeQueue(q,x);
17、 /* 出队*/if(q-front!=NULL)root=LQueueGetFront(q);/* root为队头元素 */start=1; /* 标记已出现过“”*/flag=0; /* “”后面的结点一定为左孩子 */strcpy(str,familyi);家族关系查询系统10i+;return tree; /* 返回树*/2.3 打开一个家族关系 首先输入家族关系名,以家族名为文件名打开文件,如果家族关系不存在,返回空;如果存在,文件打开,读取文件。将文件的每行信息依次存储在数组 family【】中。/* 打开一个家族关系*/TriTree *Open(DataType familyn
18、ameMAXNUM)int i=0,j=0;DataType ch;FILE *fp;TriTree *t;strcpy(fname,familyname); /* 以家族名为文本文件名存储*/strcat(fname,“.txt“);fp=fopen(fname,“r“); /* 以读取方式打开文件*/ if(fp=NULL) /* 文件不存在*/printf(“%s 的家族关系不存在!n“,familyname);return NULL;elsech=fgetc(fp); /* 按字符读取文件*/while(ch!=EOF) /* 读到文件尾结束*/if(ch!=n) /* ch不为一个结
19、点信息的结尾 */familyij=ch; /* 将文件信息存储到family数组中*/j+; else家族关系查询系统11familyij=0; /* 字符串结束标志*/i+; /* family数组行下标后移*/j=0; /* family数组列下标归零*/ch=fgetc(fp); /* 继续读取文件信息*/fclose(fp); /* 关闭文件*/t=TriTreeCreate(family); /* 调用函数建立三叉链表*/printf(“家族关系已成功打开!n“);return t;2.4 在家族关系中查找一个成员是否存在用递归算法实现。如果树空,返回 NULL。如果根节点就是要查
20、找的成员,返回根节点;否则,递归查找它的左右子树。/* 查找一个成员是否存在*/TriTree *Search(TriTree *t,DataType str) TriTree *temp;if(t=NULL) /* 如果树空则返回NULL */return NULL;else if(strcmp(t-data,str)=0) /* 如果找到返回该成员指针 */return t; else /* 如果没找到遍历左右子树进行查找*/temp=Search(t-lchild,str); /* 递归查找*/if(temp) /* 结点不空则查找*/return(Search(t-lchild,str
21、);elsereturn(Search(t-rchild,str);2.5 向家族中添加一个新成员家族关系查询系统12基本思想:添加的新成员要根据其双亲确定其在家族中的位置。首先判断该双亲是否在此家族关系中,若存在则查找其双亲,将新结点插入其双亲的最后一个孩子之后;若没有孩子,则直接作为左孩子插入。以写入的方式打开文件,如果成功打开,则更新family数组中的信息,并查找新成员的双亲所在位置和其对应的“”个数,如果“”个数小于双亲位置,则添加“”实质相等,新成员不插入到最后“”之前。最后将family数组中信息写入文件保存,关闭文件。/* 添加一个新成员*/void Append(TriTre
22、e *t)int i=0,j,parpos=1,curpos,num,end=0,count=-1;DataType chiMAXNUM,parMAXNUM;/* 存储输入的孩子和其双亲结点*/TriTree *tpar,*temp;FILE *fp;printf(“请输入要添加的成员和其父亲,以回车分隔! n. “);gets(chi);printf(“. “); /* 以点提示符提示继续输入*/gets(par);tpar=Search(t,par); /* 查找双亲结点是否存在*/if(!tpar)printf(“%s 该成员不存在!n“);else /* 存在则添加其孩子*/temp=
23、(TriTree *)malloc(sizeof(TriTree);/* 申请空间*/temp-parent=tpar; strcpy(temp-data,chi);temp-lchild=NULL; /* 新结点左右孩子置空*/temp-rchild=NULL;if(tpar-lchild) /* 成员存在左孩子 */tpar=tpar-lchild; /* 遍历当前成员左孩子的右子树*/while(tpar-rchild) /* 当前结点右孩子存在 */tpar=tpar-rchild; /* 继续遍历右孩子*/tpar-rchild=temp; /* 将新结点添家族关系查询系统13加到所
24、有孩子之后*/ else /* 没有孩子则直接添加*/tpar-lchild=temp;fp=fopen(fname,“w“); /* 以写入方式打开文件 */if(fp)while(strcmp(par,familyi)!=0 /* parpos计数*/i+; /* family数组行下标后移*/i=0; /* family数组行下标归*/while(familyi0!=#)if(familyi0=) /* 查找 “”的个数,第一个不计*/count+; /* count累加个数*/if(count=parpos) /* 说明此“”与其前一个“”之前为par的孩子*/curpos=i; /*
25、 curpos计当前位置*/i+; /* family数组行下标后移*/if(count=curpos;j-) /* 当前位置到数组最后的全部信息后移一行*/strcpy(familyj+1,familyj);strcpy(familycurpos,chi); /* 将新结点存储到“”的前一行*/if(end=1) /* 若end 为,则数组末尾下标后移num 位*/ i=i+num;for(j=0;jdata);家族关系查询系统152.6.2 查找一个成员的所有祖先路径查找一个成员的所有祖先路径,需要从它的双亲一直向上查找到根结点。基本思想:对与结点t,先判断它是否是根结点(根节点的双亲是N
26、ULL),如果是根结点,直接输出它本身;如果不是,查找它的双亲指针指向的结点,将双亲信息输出。继续查找,直到找到根结点。/* 查找一个成员的所有祖先*/void AncesstorPath(TriTree *t)if(t-parent=NULL) /* 若该成员为祖先,则直接输出*/printf(“%s 无祖先!n“,t-data);else /* 否则继续查找祖先*/printf(“%s 所有祖先路径:%s“,t-data,t-data);while(t-parent!=NULL)/* 若当前成员的双亲不是祖先,则继续查找*/printf(“ %s“,t-parent-data); /* 访
27、问当前成员的双亲*/t=t-parent; /* 继续循环查找*/printf(“n“);2.6.3 查找一个成员的双亲基本思想:先判断结点t是否是根结点,过不是根结点,直接输出该结点双亲指针的结点信息;若是根结点,输出提示信息,结点无双亲。家族关系查询系统16/* 查找一个成员的双亲*/void Parent(TriTree *t)if(t-parent!=NULL) /* 若该成员为祖先,则无双亲*/printf(“%s 的双亲为%sn“,t-data,t-parent-data);elseprintf(“%s 无双亲!n“,t-data);2.6.4 确定一个成员是第几代确定一个成员是第
28、几代,只要知道从它本身到根结点包括的祖先个数就可。因而对于跟结点t,从它本身开始一直向上查找到根结点,查找的过程中用变量count(初值为1)计数,最后输出 count。/* 确定一个成员是第几代*/void Generation(TriTree *t)int count=1; /* 计数*/DataType strMAXNUM;strcpy(str,t-data); /* 存储当前信息*/while(t-parent!=NULL)/* 查找其双亲*/count+; /* 累加计数*/t=t-parent;printf(“%s 是第%d 代!n“,str,count);2.6.5 查找一个成员
29、的兄弟一个成员的为其双亲除了该成员以外的所有孩子。基本思想:对于结点t,先判断它是否是跟结点,若是根结点,则无兄弟;若不是根结点,则找到结点t的双亲。接着判断双亲的左孩子和左孩子的兄弟是否都存在(若只有左孩子,左孩子就是要查找的这个成员),如果都不存在,则无兄弟;如果家族关系查询系统17都存在,对双亲的左孩子操作。若左孩子不是要查找的这个成员,则将结点信息输出。接下来查找左孩子的右兄弟,判断当前结点是否是要查找的这个成员,若不是,则将结点信息输出,继续查找当前结点的右兄弟,直到NULL为止。/* 查找一个成员的兄弟*/void Brothers(TriTree *t,DataType str)
30、 /* 查找兄弟*/ if(t-parent!=NULL) /* 若该结点是祖先,则无兄弟*/t=t-parent; /* 该结点的兄弟即为其双亲除该成员以外的所有孩子*/if(t-lchildt=t-lchild; while(t) /* 遍历当前成员左孩子的右子树 */ if(strcmp(t-data,str)!=0) /* 遍历右子树,选择输出*/printf(“%s “,t-data); /* 访问当前结点*/t=t-rchild;printf(“n“);elseprintf(“%s 无兄弟!n“,str);elseprintf(“%s 无兄弟!n“,str);2.6.6 查找一个成
31、员的堂兄弟一个成员的堂兄弟为其双亲的双亲结点的所有孩子的孩子(该成员除外).家族关系查询系统18基本思想:如果结点t的双亲和双亲的双亲(爷爷)都存在,首先考虑爷爷的左孩子。如果爷爷的左孩子不是结点t的双亲,那么爷爷还有其他孩子。现对爷爷的左孩子的左孩子访问,如果不是结点t就输出。同样,对爷爷左孩子的左孩子的右孩子、右孩子的右孩子一直访问下去,直到无右孩子为止。如果爷爷还有其他孩子,那么就对爷爷的左孩子的右孩子、爷爷的左孩子的右孩子的右孩子-即对爷爷的其他孩子做相同的处理。/* 查找一个成员的堂兄弟*/void Consin(TriTree *t)int flag=0;TriTree *ch=t
32、;TriTree *temp;if(t-parent/* 当前结点等于其祖先的第一个孩子*/while(t) /* 存在则继续查找 */ if(strcmp(t-data,ch-parent-data)!=0)/* 不是同一结点*/if(t-lchild) /* 当前结点存在左孩子 */temp=t-lchild;while(temp) /* 遍历当前结点左孩子的右子树*/ if(strcmp(temp-data,ch-data)!=0)if(!flag) /* 第一次输入时先输出下句*/printf(“%s 的所有堂兄弟有:“,ch-data);printf(“%s “,temp-data)
33、;/* 访问当家族关系查询系统19前成员*/flag=1;temp=temp-rchild; /* 继续遍历右孩子*/t=t-rchild; /* 继续遍历右孩子*/printf(“n“);if(!flag) /* 标志没有输出结点 */printf(“%s 无堂兄弟!n“,ch-data);2.6.7 查找一个成员的所有孩子一个成员的所有孩子包括左孩子和左孩子的右孩子、左孩子的右孩子的右孩子-直到右孩子为空为止。基本思想:首先判断结点t是否有左孩子,如果没有,输出没有孩子;如果有左孩子,输出左孩子的信息,然后判断左孩子的右孩子是否为空,若不为空(存在右孩子),输出左孩子的右孩子的信息,接着循
34、环判断结点是否有右孩子,有就输出,直到右孩子为空。/* 查找一个成员的所有孩子*/void Children(TriTree *t) /* 遍历左孩子*/ if(t-lchild) /* 当前结点存在左孩子 */printf(“%s 的所有孩子有:“,t-data);t=t-lchild; /* 遍历当前成员左孩子的右子树*/while(t) /* 不空*/ printf(“%s “,t-data);/* 访问当前成员*/t=t-rchild; 家族关系查询系统20printf(“n“);elseprintf(“%s 无孩子!n“,t-data);/* 中序遍历一棵树*/void InOrde
35、r(TriTree *t) if(t) /* 二叉树存在*/InOrder(t-lchild); /* 中序遍历左子树 */printf(“%s “,t-data);/* 访问成员*/InOrder(t-rchild); /* 中序遍历右子树*/2.6.8 查找一个成员的子孙后代一个成员的子孙后代就是其左子树上的所有结点,所以对其左子树进行中序遍历即可。/* 查找一个成员的子孙后代*/ void Descendants(TriTree *t) /* 遍历左孩子*/ if(t-lchild) /* 当前结点存在左孩子*/printf(“%s 的所有子孙后代有:“,t-data);InOrder(
36、t-lchild); /* 中序遍历当前结点的左右子树 */printf(“n“);elseprintf(“%s 无后代!n“,t-data);2.7 各软件之间的调用方式该软件各个模块的调用主要是通过声明函数,和定义函数 家族关系查询系统21再通过主函数调用实现的。主函数:/* 主控函数*/int main(int argc,char* argv)DataType strMAXNUM=“0“,input40;int i,j,flag,start=0,pos,tag1,tag2;TriTree *temp,*tree=NULL;while(1)printf(“t欢迎使用家族关系查询系统!n“)
37、;printf(“t请输入与之匹配的函数和参数,如parent(C)n“ );printf(“t 1.新建一个家庭关系: Create(familyname) 参数为字符串n“);printf(“t 2.打开一个家庭关系: Open(familyname) 参数为字符串n“);printf(“t 3.添加新成员的信息: Append() 无参数n“ );printf(“t 4.查找一个成员的祖先: Ancesstor(name) 参数为字符串n“);printf(“t 5.查找一个成员的祖先路径:AncesstorPath(name) 参数为字符串n“ );printf(“t 6.确定一个成员
38、是第几代: Generation(name) 参数为字符串n“);printf(“t 7.查找一个成员的双亲: Parent(name) 参数为字符串n“);printf(“t 8.查找一个成员的兄弟: Brothers(name) 参数为字符串n“);printf(“t 9.查找一个成员的堂兄弟: Consin(name) 参数为字符串n“);printf(“t10.查找一个成员的孩子: Children(name) 参数为字符串n“);printf(“t11.查找一个成员的子孙后代:Descendants(name) 参数为字符串n“);printf(“t12.退出系统: Exit() 无
39、参数n? “);家族关系查询系统22gets(input); /* input数组存放输入的函数和参数*/j=0,tag1=0,tag2=0;for(i=0;i=4/* 若存在则返回结点 */if(!temp) /* 若不存在则返回*/printf(“该成员不存在!n“);continue;switch(flag) /* 根据flag标记调用函数 */case 1: tree=Create(str); 家族关系查询系统24break;case 2:tree=Open(str); break;case 3: Append(tree); break;case 4:Ancesstor(tree);
40、break;case 5: AncesstorPath(temp); break;case 6: Parent(temp); break;case 7:Generation(temp); break;case 8: Brothers(temp,str); break;case 9: Consin(temp); break;case 10: Children(temp); break;case 11:Descendants(temp); break;case 12: exit(OK);return 0;家族关系查询系统253 调试结果调试运行后,显示主界面1)新建一个家族关系家族关系查询系统26
41、2)添加一个新成员3)查找一个成员的兄弟4)查找一个成员的堂兄弟家族关系查询系统275)查找一个成员的双亲6)查找一个成员的孩子7)查找一个成员是第几代8)查找一个成员的祖先路径9)查找一个成员的子孙后代家族关系查询系统284 结果分析本程序巧妙地将家族关系信息转化为二叉树信息,利用队列来创建三叉链表,存储家族关系信息,能够将信息存入文件中,以便下次访问,能对当前家族添加新成员,实现动态查询。在家族关系查询中包含了许多查询功能,可通过输入不同的命令和参数有选择的实现各种查询,操作方便,实用性强。在编写程序时,可以加入修改成员信息功能,并能及时更新保存。因此可以在此基础上更好的扩展程序,增强其实
42、用性。5 课程设计总结通过本次课程设计,让我能够对C语言和数据结构有了一次比较深入的复习和整理,同时也加深了我对程序的的理解,特别是对一个大程序,来怎样对它进行组织编写。本次课程设计使我认清了今后应该努力的方向,同时也暴露了自己在平常学习中的不足。只注重课本上的知识,而没有把课本上的知识付诸实践。数据结构是一门比较抽象的学科,而通过此次设计是我对数据结构有了一个更深的理解。在编写某个具体程序时,应根据其数据特点选择适当的逻辑结构、存储结构及其相应的算法。针对不同的算法,应分析其时间和空间复杂度,选择最优算法,提高算法执行效率。在编写程序时,应特别注意编写的程序结构要清楚,并且正确易读,要符合规范,平常就要这样要求自己。本课程设计在一定程度上也培养了自己的数据抽象能力。由于本次我设计的是家族关系查询系统,所涉及的功能和模块比较多,因此我们小组三人合作完成了本次课程设计,同时使我认识到团队合作的重要性。在此我对小组其他的两个成员的努力和老师的指导表示衷心的感谢!总之,本次课程设计使我受益匪浅,学到了不少课本之外的知识,也认识到自己的不足。在以后需要加强自身训练,学以致用,学会自我总结,吸取教训,积累经验,在学习和实践中不断提升自己!家族关系查询系统29