一、课程设计任务要求

【问题描述】

家谱记载了一个家族的世系繁衍及重要人物事迹。使用树型结构对家谱进行管理,实现查看祖先和子孙个人信息,插入家族成员,删除家族成员的功能

【基本要求】

(1)采用树形结构完成对家谱成员信息的建立,可利用孩子兄弟表示方法表示树型结构

(2)完成家谱成员信息查找、插入、修改、删除功能

(3)判断两个人的家族关系

(4)进行子孙、祖先、堂兄弟关系的查询

【拓展要求】

(1)实现树的层次遍历,显示家族每一代的成员

(2)打印家谱的树型结构操作

(3)判断两个成员是否属于直系或旁系三代关系

(4)自行设计家谱的其他操作

二、已完成的项目及完成程度 

1.利用孩子兄弟表示方法表示树型结构完成对家谱成员信息的建立

2.完成家谱成员信息查找、插入、修改、删除功能

3.判断两个人的家族关系

4.进行子孙、祖先、堂兄弟关系的查询

5.利用树的层次遍历表示家族每一代的成员

6.打印家谱的树型结构操作

7.判断两个成员是否属于直系或旁系三代关系

三、理论依据

1.利用结构体将家谱中的一个角色的姓名,性别,出生年份,是否具有配偶以及其父辈姓名进行封装;并采用孩子兄弟法构造家谱的树型结构,以体现家谱中各人之间的关系。

2.利用队列来完成家谱的层次遍历,队列元素为CSTree类型。

3.孩子兄弟表示的家谱类似于二叉树,其firstchild相当于二叉树的左子树,其nextSibling相当于二叉树的右子树,则其前序遍历也相当于二叉树的先序遍历,则其算法思想与二叉树的先序遍历相同。

4.在本次课设中引入了文件的插入。

四、主要算法流程

1.树的层次遍历算法

(1)层次遍历,类似于图中的广度优先遍历。定义一个队列,

(2)将根结点入队,先将根结点入队列,(3)当队列不为空时循环进行如下操作:队头元素出队列,访问该元素,将它的子女结点及子女结点的所有兄弟结点入队列。

2树的先根次序遍历。

  1. 类似于深度优先遍历,先根次序遍历是先访问根结点,
  2. 再先根递归访问它的所有子树;后根次序遍历是先后根递归遍历它的所有子树,再访问根节点。
  3. 使用 fopen( ) 函数来创建一个新的文件或者打开一个已有的文件;FILE *fopen( const char * filename, const char * mode );.r 打开一个已有的文本文件,允许读取文件.

五、主要代码

1.通用头文件common.h

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<math.h>

#include<malloc.h>

#define TRUE 1

#define FALSE 0

#define OK 1

#define ERROR 0

typedef int Status;

2,结构类型定义头文件CSTreelist.h

#include"common.h"



typedef struct{

    char name[20];//姓名

    int sex;//性别 1为男性 0为女性

    char mate[20];//配偶姓名  

    int seniority;//辈分

    int birthyear;//年龄

    int alive;//1为在世,0为已过世

}TElemType;



typedef struct CSTNode

{

    TElemType data;

    struct CSTNode *firstChild;

struct CSTNode *nextSibling;

struct CSTNode *father;

}CSTNode,*CSTree,*CSForest;//孩子结点类型





typedef struct LNode{               //链表和链表结点类型

    CSTree data;                     //数据域

    struct LNode *next;             //指针域

}LNode, *LinkList;



void createCSTNode(CSTree*T);//创建结点

void initCSTNode(CSTree*T);//初始化结点

void searchNode(CSForest T,char name[20],CSTree*A);//通过姓名查找结点

void insertNode(CSTree*father,CSTree*child);//插入节点

void deleteCSTNode(CSTree *T);//删除节点(包括他的子树)

void destroy(CSTree*T);//销毁某树



void showCSTNode(CSTree*T);

void changeBirthyear(CSTree *T);

void changeFather(CSTree *P,CSForest T);

void refreshSeniority(CSForest*T);

void changeCSTNodeimf(CSTree*P,CSForest T);

Status InitQueue(LinkList *L);                                  //初始化队列

LNode* CreateNode(CSTree t);                                     //新建一个结点

Status Enqueue(LNode *p,CSTree t);                               //元素q入队列

Status Dequeue(LNode *p,CSTree *q);                            //出队列,并以q返回值

Status IfEmpty(LinkList L);                                     //队列判空

void DestroyQueue(LinkList L);                                  //销毁队列

Status Traverse(CSTree T,LinkList L);        //用队列遍历输出树

Status PrintTree(CSTree t);

void changeNode();





void inorderCousin(CSTree T,CSTree A);

void PrintAsTree(CSTree T);



void init();//初始化

void insertNewNode();//插入人物

void search();

void deleteNode();

void changeNode();

void relationship();

void searchrelacen();

void Meau0();//此程序说明界面

void Meau1();//菜单函数选择界面
  1. CSTOperate.cpp文件
#include"common.h"



typedef struct{

    char name[20];//姓名

    int sex;//性别 1为男性 0为女性

    char mate[20];//配偶姓名  

    int seniority;//辈分

    int birthyear;//出生年份

    int alive;//1为在世,0为已过世

}TElemType;



typedef struct CSTNode

{

    TElemType data;

    struct CSTNode *firstChild;

struct CSTNode *nextSibling;

struct CSTNode *father;

}CSTNode,*CSTree,*CSForest;//孩子结点类型





typedef struct LNode{               //链表和链表结点类型

    CSTree data;                     //数据域

    struct LNode *next;             //指针域

}LNode, *LinkList;



void createCSTNode(CSTree*T);//创建结点

void initCSTNode(CSTree*T);//初始化结点

void searchNode(CSForest T,char name[20],CSTree*A);//通过姓名查找结点

void insertNode(CSTree*father,CSTree*child);//插入节点

void deleteCSTNode(CSTree *T);//删除节点(包括他的子树)

void destroy(CSTree*T);//销毁某树



void showCSTNode(CSTree*T);

void changeBirthyear(CSTree *T);

void changeFather(CSTree *P,CSForest T);

void refreshSeniority(CSForest*T);

void changeCSTNodeimf(CSTree*P,CSForest T);

Status InitQueue(LinkList *L);                                  //初始化队列

LNode* CreateNode(CSTree t);                                     //新建一个结点

Status Enqueue(LNode *p,CSTree t);                               //元素q入队列

Status Dequeue(LNode *p,CSTree *q);                            //出队列,并以q返回值

Status IfEmpty(LinkList L);                                     //队列判空

void DestroyQueue(LinkList L);                                  //销毁队列

Status Traverse(CSTree T,LinkList L);        //用队列遍历输出树

Status PrintTree(CSTree t);

void changeNode();





void inorderCousin(CSTree T,CSTree A);

void PrintAsTree(CSTree T);



void init();//初始化

void insertNewNode();//插入人物

void search();

void deleteNode();

void changeNode();

void relationship();

void searchrelacen();

void Meau0();//此程序说明界面

void Meau1();//菜单函数选择界面
  1. main.cpp
#include"CSTreelist.h"



CSForest T;



CSTree A,B;

void init(){

    createCSTNode(&T);

    FILE*r;

    r= fopen("H.txt","r");

    if(r==NULL)

    {

        printf("打开文件失败!");

    }

    A=NULL;

    B=NULL;

    char fatherName[100];

    createCSTNode(&A);

    while(fscanf(r,"%s %d %s %d %d %s",&A->data.name,&A->data.sex,&A->data.mate,&A->data.birthyear,&A->data.alive,fatherName)!=EOF)

    {  

        printf("%s %d %s %d %d %s",A->data.name,A->data.sex,A->data.mate,A->data.birthyear,A->data.alive,fatherName);

        

        searchNode(T,fatherName,&B);

        if(B==NULL){

            printf("此人的父亲不在家谱里!\n");

            free(A);

        }

        if(B->data.seniority!=0&&B->data.birthyear>=A->data.birthyear){

            printf("出生不能比父亲早\n");

            free(A);

        }

        else {

            printf("insert\n");

            insertNode(&B,&A);

            }

        B=NULL;

        A=NULL;

        createCSTNode(&A);

    }

    free(A);

    A=NULL;

    fclose(r);

}





void insertNewNode(){

    char fatherName[100];

    A=NULL;

    B=NULL;

    createCSTNode(&A);

    printf("请输入:姓名 性别(1/0) 配偶姓名 出生年份  生存状况(1/0) 父亲姓名\n ");

    printf(" 例如: 刘星  1         无       1560       1           刘六\n ");

    scanf("%s %d %s %d %d %s",&A->data.name,&A->data.sex,&A->data.mate,&A->data.birthyear,&A->data.alive,fatherName);

    if(A->data.sex!=1&&A->data.sex!=0){

        printf("性别输入有误!\n");

        free(A);

        A=NULL;

        return;

    }

    if(A->data.alive!=1&&A->data.alive!=0){

        printf("生存状况输入有误!\n");

        free(A);

        A=NULL;

        return;

    }

    if(A->data.birthyear>2021||A->data.birthyear<0){

        printf("出生年份输入有误!\n");

        free(A);

        A=NULL;

        return;

    }

    searchNode(T,fatherName,&B);

    if(B==NULL){

        printf("此人的父亲不在家谱里!\n");

        free(A);

        A=NULL;

        return;

    }

    if(B->data.seniority!=0&&B->data.birthyear>=A->data.birthyear){

        printf("出生不能比父亲早\n");

        free(A);

        A=NULL;

        B=NULL;

        return;

    }

    insertNode(&B,&A);

    printf("插入成功\n");

    A=NULL;

    return;

}



void search(){

    char name[20];

    printf("请输入姓名:");

    scanf("%s",name);

    A=NULL;

    searchNode(T,name,&A);

    if(A==NULL){

        printf("不存在名为%s的人\n",name);

        return;

    }

    showCSTNode(&A);

    A=NULL;

}



void deleteNode(){

    A=NULL;

    char name[20];

    printf("请输入姓名:");

    scanf("%s",name);

    searchNode(T,name,&A);

    if(A==NULL){

        printf("不存在名为%s的人\n",name);

        return;

    }

    deleteCSTNode(&A);

    A=NULL;

printf("已删除\n");

}





void changeNode(){

    A=NULL;

    char name[100];

    printf("请输入姓名:");

    scanf("%s",name);

    searchNode(T,name,&A);

    if(A==NULL){

        printf("不存在名为%s的人\n",name);

        return;

    }

    changeCSTNodeimf(&A,T);

    A=NULL;

}





void relationship(){

    B=NULL;

    A=NULL;

    char name[20];

    printf("请输入姓名:");

    scanf("%s",name);

    searchNode(T,name,&B);

    if(B==NULL){

        printf("不存在名为%s的人\n",name);

        return;

    }

int choice;

    C:system("cls");

    showCSTNode(&B);

    printf("\n");

    printf("你想选择哪个关系?\n");

    printf("\n");

    printf("********************************\n");

    printf("\n");

    printf("    1.祖先     2.后代\n");

    printf("\n");

    printf("    3.堂兄弟       \n");

    printf("\n");

    printf("********************************\n");

    printf("\n");         

printf("请输入序号:\n");

while(1){

scanf("%d",&choice);

if(choice>0&&choice<4)

break;

else{

printf("请重新输入(1-3)");

}

}

    switch(choice)

    {

        case 1:

            A=B->father;

            for(;A->data.seniority!=0;A=A->father){

                printf("[%d|%d|%s]\n",A->data.seniority,A->data.birthyear,A->data.name);

            }

            system("pause"); goto C;

        case 2:

            PrintTree(B);

            system("pause"); goto C;

        case 3:

            inorderCousin(T,B);

            system("pause"); goto C;

       

    }

    B=NULL;

    A=NULL;

    return;

}



void searchrelacen(){//三代关系

    A=NULL;

    B=NULL;

    char name[100];

    printf("请输入姓名1:");

    scanf("%s",name);

    searchNode(T,name,&A);

    if(A==NULL){

        printf("不存在名为%s的人\n",name);

        return;

    }

    printf("请输入姓名2:");

    scanf("%s",name);

    searchNode(T,name,&B);

    if(B==NULL){

        printf("不存在名为%s的人\n",name);

        A=NULL;

        return;

    }

    CSTree temp=NULL;

    if(A->data.birthyear>B->data.birthyear){

        temp=B;

        B=A;

        A=temp;

    }

    if((A!=T&&B!=T)&&A==B->father){

        printf("%s是%s的父亲\n",A->data.name,B->data.name);

        if(B->data.sex){

            printf("%s是%s的儿子\n",B->data.name,A->data.name);

        }

        else

        {

            printf("%s是%s的女儿\n",B->data.name,A->data.name);

        }   

        printf("关系:三代内直系\n");

    }

    else if((A!=T&&B!=T)&&A==B->father->father){

        printf("%s是%s的爷爷\n",A->data.name,B->data.name);

        if(B->data.sex){

            printf("%s是%s的孙子\n",B->data.name,A->data.name);

        }

        else

        {

            printf("%s是%s的孙女\n",B->data.name,A->data.name);

        }   

        printf("关系:三代内直系\n");

    }

    else if((A->data.seniority>1&&B->data.seniority>1)&&A->father==B->father){

        

        if(A->data.sex){

            printf("%s是%s的堂兄\n",A->data.name,B->data.name);

        }

        else

        {

            printf("%s是%s的堂姐\n",A->data.name,B->data.name);

        }   

        if(B->data.sex){

            printf("%s是%s的堂弟\n",B->data.name,A->data.name);

        }

        else

        {

            printf("%s是%s的堂妹\n",B->data.name,A->data.name);

        }   

        printf("关系:三代内旁系\n");

    }

    else if((A->data.seniority>1&&B->data.seniority>1)&&A->father==B->father->father){

        

        if(A->data.sex){

            printf("%s是%s的叔叔\n",A->data.name,B->data.name);

        }

        else

        {

            printf("%s是%s的姑妈\n",A->data.name,B->data.name);

        }   

        if(B->data.sex){

            printf("%s是%s的侄子\n",B->data.name,A->data.name);

        }

        else

        {

            printf("%s是%s的侄女\n",B->data.name,A->data.name);

        }   

        printf("关系:三代内旁系\n");

    }

    else

    {

        printf("关系:%s,%s两者非三代内旁系或直系\n",A->data.name,B->data.name);

    }

    A=NULL;

    B=NULL;

    

}



void Meau0(){

printf("\t| -----------------------------------------------------------  |\n");

printf("\t|              家谱系统                       |\n");

printf("\t| -----------------------------------------------------------  |\n");

printf("\t| ***********************************************************  |\n");

printf("\t| *                                                         *  |\n");

printf("\t| *      提示:此家谱采用孩子兄弟表示的树存储形式            *  |\n");

printf("\t| *                                                         *  |\n");

printf("\t| ***********************************************************  |\n");

}

void Meau1(){

printf("\t| -----------------------------------------------------------  |\n");

printf("\t|                       操作选择界面                           |\n");

printf("\t| -----------------------------------------------------------  |\n");

printf("\t| ***********************************************************  |\n");

printf("\t| *                          *                              *  |\n");

printf("\t| *      1.新建人物          *     2.删除人物               *  |\n");

printf("\t| *                          *                              *  |\n");

printf("\t| ***********************************************************  |\n");

printf("\t| *                          *                              *  |\n");

printf("\t| *      3.修改人物信息      *     4.查找人物               *  |\n");

printf("\t| *                          *                              *  |\n");

printf("\t| ***********************************************************  |\n");

printf("\t| *                          *                              *  |\n");

printf("\t| *      5.人物关系查询      *     6. 判断两人关系          *  |\n");

printf("\t| *                          *                              *  |\n");

printf("\t| ***********************************************************  |\n");

printf("\t| *                          *                              *  |\n");

printf("\t| *      7.打印树状家谱      *     8.层次遍历家谱           *  |\n");

printf("\t| *                          *                              *  |\n");

printf("\t| ***********************************************************  |\n");

printf("\t----------------------------------------------------------------\n");

}        

int main()

{



int choose;//功能选择按钮

Meau0();

system("pause");

system("cls");

init();

system("pause");

system("cls");

do

{

Meau1();

printf("请选择相应的操作:");

scanf("%d", &choose);

while (1)

{

if (choose < 1 || choose>9)

{

printf("输入有误,请根据你的功能重新选择对应操作:");

scanf("%d", &choose);

}

else break;

}

switch (choose)

{



case 1:system("cls"); insertNewNode(); break;//插入人物

case 2:system("cls"); deleteNode(); break;//删除人物

case 3:system("cls"); changeNode(); break;//修改人物

case 4:system("cls"); search();; break;//查找人物

case 5:system("cls"); relationship();break;//判断两人关系

case 6:system("cls"); searchrelacen();break;//查三代关系

case 7:system("cls"); PrintAsTree(T); break;//打印人物

case 8:system("cls"); PrintTree(T);break;//层次遍历

}

int t;

printf("是否返回选择界面(1:是,0:否):");

scanf("%d", &t);

while (1)

{

if (t == 0) {

printf("已成功退出系统。\n");

exit(0);

}

else if (t == 1) break;

else {

printf("选择错误,请重新确定你的输入:");

scanf("%d", &t);

}

}

system("cls");

} while (1);

return 0;

}
  1. H.txt文档
张一 1 无 1500 1 无

张二 1 无 1501 1 无

张三 1 李一 1520 1 张一

张四 1 无 1522 1 张二

张五 1 李二 1523 1 张三

张六 1 李三 1558 1 张三

张七 1 无 1569 1 张四

张八 1 李四 1583 1 张七

张九 0 无 1584 1 张七

张十 1 无 1585 1 张五

张十一 1 无 1586 1 张五

张十二 0 无 1587 1 张五

张十三 0 无 1596 1 张六

张十四 1 李五 1598 0 张六

张十五 1 无 1600 1 张六

张十六 1 无 1605 1 张六

张十七 0 无 1620 1 张六

张十八 1 李六 1625 1 张八

张十九 1 无 1635 0 张十四

六、实验结果

实验用例为H.txt

(1)家谱系统界

1.插入人物

成功案例:

  • 不成功案例:

2.删除人物

成功案例:删除张时

不成功案例:

3.修改人物信息

1名字

2性别

3配偶名字

4出生年份

5辈分

4.查找

成功案例:

不成功案例:

人物关系查询

1.祖先

2后代

3堂兄弟

5.判断两人关系

6.打印树状家谱

7.层次遍历打印家谱

七、小结

通过此次数据结构的课程设计,直正达到了学与用的结合,增强了我对二叉树方面应用的理解和掌握。

虽然是用孩子兄弟法表示家谱,但其本质也是特殊的二叉树;在实验过程中,从需求分析,到概念设计,懂得了不少有关项目建设的知识,包括,插入、删除、修改、查询之间的联系,都用到很多关

键的知识点。整个程序和以往所编的程序较大的区别就是要求从文件读入信息,文件是 C语言中较为重要的部分,通过课程设计,加深了对文件知识的掌握。我的课程设计原先是只能从固定的一个文件读入图的

信息,经过修改,它可以通过直接代码执行来提取文件,使程序更加灵活,功能更加完善。在学习过程中,我也能过上网查了不少资料,学以致用,自我创新,独立完成了这份自己的报告,从中在学到用,从用又到学,不断修改,系统更新。虽然不能达到完善系统,但也做到了尽善尽美,加强理论学习对完善系统会有很多帮助。通过此次课程设计还提高了一点改错能力,对于一些常见问题加深了印象。每次课程设计都会有多多少少的收获,这些收获将成为以后学习中一笔不可或缺的财富。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐