数据结构课程设计家族关系查询系统.docx
- 文档编号:5647889
- 上传时间:2022-12-30
- 格式:DOCX
- 页数:28
- 大小:96.30KB
数据结构课程设计家族关系查询系统.docx
《数据结构课程设计家族关系查询系统.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计家族关系查询系统.docx(28页珍藏版)》请在冰豆网上搜索。
数据结构课程设计家族关系查询系统
1课程设计介绍
1.1课程设计项目简介
家谱是一种以表谱形式,记载一个以血缘关系为主体的家族世系繁衍和重要人物事迹的特殊图书载体。
家谱是中国特有的文化遗产,是中华民族的三大文献之一,属珍贵的人文资料,对于历史学,民俗学,人口学,社会学和经济学的深入研究,均有不可替代的重要功能。
本项目对家谱管理进行简单的模拟,以实现查看祖先和子孙个人信息、插入家族成员等功能。
1.2课设题目分析
本程序的实质是完成对家谱成员信息的建立、查找、插入等功能。
可以首先定义家族成员的数据结构,然后将每个功能写成一个函数来完成对数据的操作,最后完成主函数以验证各个函数功能并得出运行结果。
本程序包含以下几个模块
(1)建立家族关系树。
此模块将构建一个家族关系,对数据初始化,构造关系树并录入数据一遍后续程序使用。
(2)添加新成员。
此模块将添加一个新成员,实现对家族关系的修改。
(3)家族关系的查询。
此模块将实现对家族不同关系的查询
(4)主程序模块。
此模块实现整个程序的进入和进出,以及各种初始化处理。
(5)
1.3课程题目原理与数据结构
因为家族的成员之间存在一个对多个的层次结构关系,所以不能用线性表来表示和实现。
家谱从形状上看像一颗倒长的树,所以用树结构来表示比较合适。
树形结构是一类非常重要的非线性数据结构,直观看来树是以分支关系定义的层次结构。
因此本课程设计可以采用的数据结构有树状结构和队列。
树状结构采用三叉链表来实现,队列采用链式队列实现。
1.4功能分析说明图
2分析与实现
2.1基本数据结构和栈队的操作
2.1.1结点基本数据结构和链队的定义
/*家族关系树实现*/
#include
#include
#include
#include
#include
#include
#include
#include
#defineTRUE1
#defineFALSE0
#defineOK1
#defineERROR-1
#defineINFEASIBLE-1
typedefcharDataType;
#defineMAXNUM20
typedefstructTriTNode/*树的三叉链表存储结构*/
{
DataTypedata[MAXNUM];
structTriTNode*parent;/*双亲*/
structTriTNode*lchild;/*左孩子*/
structTriTNode*rchild;/*右孩子*/
}TriTree;
typedefstructNode/*队列的结点结构*/
{
TriTree*info;
structNode*next;
}Node;
typedefstruct/*链接队列类型定义*/
{
structNode*front;/*头指针*/
structNode*rear;/*尾指针*/
}LinkQueue;
DataTypefname[MAXNUM],family[50][MAXNUM];/*全局变量*/
2.1.2链队的基本操作
LinkQueue*LQueueCreateEmpty()/*建立一个空队列*/
{
LinkQueue*plqu=(LinkQueue*)malloc(sizeof(LinkQueue));
if(plqu!
=NULL)
plqu->front=plqu->rear=NULL;
else
{
printf("内存不足!
\n");
returnNULL;
}
returnplqu;
}
intLQueueIsEmpty(LinkQueue*plqu)/*判断链接表示队列是否为空队列*/
{
return(plqu->front==NULL);
}
voidLQueueEnQueue(LinkQueue*plqu,TriTree*x)/*进队列*/
{
Node*p=(Node*)malloc(sizeof(Node));
if(p==NULL)
printf("内存分配失败!
\n");
else
{
p->info=x;
p->next=NULL;
if(plqu->front==NULL)/*原来为空队*/
plqu->front=p;
else
plqu->rear->next=p;
plqu->rear=p;
}
}
intLQueueDeQueue(LinkQueue*plqu,TriTree*x)/*出队列*/
{
Node*p;
if(plqu->front==NULL)
{
printf("队列空!
\n");
returnERROR;
}
else
{
p=plqu->front;
x=p->info;
plqu->front=plqu->front->next;
free(p);
returnOK;
}
}
TriTree*LQueueGetFront(LinkQueue*plqu)/*在非空队列中求队头元素*/
{
return(plqu->front->info);
}
2.2建立家族关系
2.2.1建立家族关系并存入文件
基本思想:
首先输入家族关系的名称,以此名称为文件名,建立文本文件接下来按层次输入结点信息,输入一个在文件中写入一行同时将输入的信息保存
到二位字符数组family中。
字符数组family是全局变量,存储临时信息.注意,输入时每个结点信息占一行,一个结点有多个兄弟,以“@”作为兄弟结束标志,结点若无孩子,直接以“@”代替。
依次输入各节点信息,以“#”作为结束标志。
最后使用函数CreateTriTree建立家族关系树.
TriTree*Create(DataTypefamilyname[MAXNUM])/*建立家族关系并存入文件*/
{
inti=0;/*i控制family数组下标*/
DataTypech,str[MAXNUM];/*ch存储输入的y或n,str存储输入的字符串*/
TriTree*t;
FILE*fp;
strcpy(fname,familyname);/*以家族名为文本文件名存储*/
strcat(fname,".txt");
fp=fopen(fname,"r");/*以读取方式打开文件*/
if(fp)/*文件已存在*/
{
fclose(fp);
printf("%s的家族关系已存在!
重新建立请按“Y”,直接打开请按“N”\n",familyname);
ch=getchar();
getchar();/*接收回车*/
if(ch=='N'||ch=='n')
{
t=Open(familyname);/*直接打开*/
returnt;
}
}
if(!
fp||ch=='Y'||ch=='y')/*重新建立,执行以下操作*/
{
fp=fopen(fname,"w");/*以写入方式打开文件,不存在则新建*/
printf("请按层次输入结点,每个结点信息占一行\n");
printf("兄弟输入结束以“@”为标志,结束标志为“#”\n.");
gets(str);
fputs(str,fp);
fputc('\n',fp);
strcpy(family[i],str);/*将成员信息存储到字符数组中*/
i++;/*family数组下标后移*/
while(str[0]!
='#')
{
printf(".");/*以点提示符提示继续输入*/
gets(str);
fputs(str,fp);/*写到文件中,每个信息占一行*/
fputc('\n',fp);
strcpy(family[i],str);/*将成员信息存储到字符数组中*/
i++;/*family数组下标后移*/
}
fclose(fp);/*关闭文件*/
t=TriTreeCreate();/*根据family数组信息创建三叉树*/
printf("家族关系已成功建立!
\n");
returnt;/*返回树*/
}
}
2.2.2建立家族关系树
基本思想:
采用指针数组作为指针,保存输入的结点地址。
队列的尾指针指向当前结点。
头指针指向当前结点的双亲结点。
输入的结点信息已存储在字符数组family中。
将信息复制到字符串数组“ch”中,如果"ch"不是“@”,则建立一个新结点。
若新结点是第一个结点,则它是根结点,将其入队,指针tree指向这个根节点;如果不是根结点,则将当前结点链接到双亲结点上,即当前结点的双亲指针就是队头元素,然后将当前结点入队列。
接着判断flag的值,如果flag=0,表示当前结点没有左孩子,那么当前结点就是双亲的左孩子。
否则,当前结点就是双亲的右孩子。
用指针root指向刚刚入队的结点。
继续复制数组family的下个元素。
如果“ch”是@。
则flag=0(因为“@”后面的第一个孩子为左孩子),同时判断“@”是否是第一次出现,如果是第一次,则令标志star=1;如果不是第一次出现。
则出队列,root指向队头元素(实际上root总是指向双亲结点)。
继续复制family的下一个元素。
知道遇到“#”结束。
函数返回指针tree。
/*建立家族关系树*/
TriTree*TriTreeCreate()
{
TriTree*t,*x=NULL,*tree,*root=NULL;
LinkQueue*q=LQueueCreateEmpty();/*建立一个空的队列,存储指向树的指针*/
inti=0,flag=0,start=0;
DataTypestr[MAXNUM];/*存放family数组中信息*/
strcpy(str,family[i]);/*复制*/
i++;/*family数组下标后移*/
while(str[0]!
='#')/*没遇到结束标志继续循环*/
{
while(str[0]!
='@')/*没遇到兄弟输入结束标志继续*/
{
if(root==NULL)/*空树*/
{
root=(TriTree*)malloc(sizeof(TriTree));/*申请空间*/
strcpy(root->data,str);
root->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->lchild=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,family[i]);
i++;
}
if(start!
=0)/*标记不是第一次出现“@”*/
{
LQueueDeQueue(q,x);/*出队*/
if(q->front!
=NULL)
root=LQueueGetFront(q);/*root为队头元素*/
}
start=1;/*标记已出现过“@”*/
flag=0;/*“@”后面的结点一定为左孩子*/
strcpy(str,family[i]);
i++;
}
returntree;/*返回树*/
}
2.3打开一个家族关系
首先输入家族关系名,以家族名为文件名打开文件,如果家族关系不存在,返回空;如果存在,文件打开,读取文件。
将文件的每行信息依次存储在数组family【】中。
/*打开一个家族关系*/
TriTree*Open(DataTypefamilyname[MAXNUM])
{
inti=0,j=0;
DataTypech;
FILE*fp;
TriTree*t;
strcpy(fname,familyname);/*以家族名为文本文件名存储*/
strcat(fname,".txt");
fp=fopen(fname,"r");/*以读取方式打开文件*/
if(fp==NULL)/*文件不存在*/
{
printf("%s的家族关系不存在!
\n",familyname);
returnNULL;
}
else
{
ch=fgetc(fp);/*按字符读取文件*/
while(ch!
=EOF)/*读到文件尾结束*/
{
if(ch!
='\n')/*ch不为一个结点信息的结尾*/
{
family[i][j]=ch;/*将文件信息存储到family数组中*/
j++;
}
else
{
family[i][j]='\0';/*字符串结束标志*/
i++;/*family数组行下标后移*/
j=0;/*family数组列下标归零*/
}
ch=fgetc(fp);/*继续读取文件信息*/
}
fclose(fp);/*关闭文件*/
t=TriTreeCreate(family);/*调用函数建立三叉链表*/
printf("家族关系已成功打开!
\n");
returnt;
}
}
2.4在家族关系中查找一个成员是否存在
用递归算法实现。
如果树空,返回NULL。
如果根节点就是要查找的成员,返回根节点;否则,递归查找它的左右子树。
/*查找一个成员是否存在*/
TriTree*Search(TriTree*t,DataTypestr[])
{
TriTree*temp;
if(t==NULL)/*如果树空则返回NULL*/
returnNULL;
elseif(strcmp(t->data,str)==0)/*如果找到返回该成员指针*/
returnt;
else/*如果没找到遍历左右子树进行查找*/
{
temp=Search(t->lchild,str);/*递归查找*/
if(temp)/*结点不空则查找*/
return(Search(t->lchild,str));
else
return(Search(t->rchild,str));
}
}
2.5向家族中添加一个新成员
基本思想:
添加的新成员要根据其双亲确定其在家族中的位置。
首先判断该双亲是否在此家族关系中,若存在则查找其双亲,将新结点插入其双亲的最后一个孩子之后;若没有孩子,则直接作为左孩子插入。
以写入的方式打开文件,如果成功打开,则更新family数组中的信息,并查找新成员的双亲所在位置和其对应的“@”个数,如果“@”个数小于双亲位置,则添加“@”实质相等,新成员不插入到最后“@”之前。
最后将family数组中信息写入文件保存,关闭文件。
/*添加一个新成员*/
voidAppend(TriTree*t)
{
inti=0,j,parpos=1,curpos,num,end=0,count=-1;
DataTypechi[MAXNUM],par[MAXNUM];/*存储输入的孩子和其双亲结点*/
TriTree*tpar,*temp;
FILE*fp;
printf("请输入要添加的成员和其父亲,以回车分隔!
\n.");
gets(chi);
printf(".");/*以点提示符提示继续输入*/
gets(par);
tpar=Search(t,par);/*查找双亲结点是否存在*/
if(!
tpar)
printf("%s该成员不存在!
\n");
else/*存在则添加其孩子*/
{
temp=(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;/*将新结点添加到所有孩子之后*/
}
else/*没有孩子则直接添加*/
tpar->lchild=temp;
fp=fopen(fname,"w");/*以写入方式打开文件*/
if(fp)
{
while(strcmp(par,family[i])!
=0&&family[i][0]!
='#')
{
if(family[i][0]!
='@')/*查找双亲在数组中位置*/
parpos++;/*parpos计数*/
i++;/*family数组行下标后移*/
}
i=0;/*family数组行下标归*/
while(family[i][0]!
='#')
{
if(family[i][0]=='@')/*查找“@”的个数,第一个不计*/
count++;/*count累加个数*/
if(count==parpos)/*说明此“@”与其前一个“@”之前为par的孩子*/
curpos=i;/*curpos计当前位置*/
i++;/*family数组行下标后移*/
}
if(count { num=parpos-count;/*添加“@”个数为num*/ for(j=i;j<=i+num;j++)/*从数组末尾添加“@”*/ strcpy(family[j],"@\0"); strcpy(family[i+num+1],"#\0");/*“#”移到数组末尾*/ strcpy(family[i+num-1],chi);/*在最后一个“@”前添加新成员*/ end=1;/*end为时标记已添加*/ } else { for(j=i;j>=curpos;j--)/*当前位置到数组最后的全部信息后移一行*/ strcpy(family[j+1],family[j]); strcpy(family[curpos],chi);/*将新结点存储到“@”的前一行*/ } if(end==1)/*若end为,则数组末尾下标后移num位*/ i=i+num; for(j=0;j<=i+1;j++)/*将数组所有信息写入文件*/ { fputs(family[j],fp); fputc('\n',fp);/*一个信息存一行*/ } fclose(fp);/*关闭文件*/ printf("添加新成员成功! \n"); } else printf("添加新成员失败! \n"); } } 2.6家族成员关系的相关查询 2.6.1查找一个家族的鼻祖 判断输入的姓名是否在该家族中存在,如果存在,则返回该家族的根节点信息。 /*查找一个家族的祖先*/ voidAncesstor(TriTree*t)/*返回树的根结点信息*/ { printf("该家族的祖先为%s\n",t->data); } 2.6.2查找一个成员的所有祖先路径 查找一个成员的所有祖先路径,需要从它的双亲一直向上查找到根结点。 基本思想: 对与结点t,先判断它是否是根结点(根节点的双亲是NULL),如果是根结点,直接输出它本身;如果不是,查找它的双亲指针指向的结点,将双亲信息输出。 继续查找,直到找到根结点。 /*查找一个成员的所有祖先*/ voidAncesstorPath(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);/*访问当前成员的双亲*/ t=t->parent;/*继续循环查找*/ } printf("\n"); } } 2.6.3查找一个成员的双亲 基本思想: 先判断结点t是否是根结点,过不是根结点,直接输出该结点双亲指针的结点信息;若是根结点,输出提示信息,结点无双亲。 /*查找一个成员的双亲*/ voidParent(TriTree*t)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 课程设计 家族 关系 查询 系统