实验二 -3 用二叉树表示家谱关系并实现各种查找功能

本文介绍了如何使用二叉树数据结构表示家谱关系,并实现查找功能,包括前序、中序和后序遍历。还涉及文件操作,如保存、读取和删除家谱记录,以及添加、修改和查询特定家谱成员的功能。
摘要由CSDN通过智能技术生成

用二叉树表示家谱关系并实现各种查找功能

目录

用二叉树表示家谱关系并实现各种查找功能

一、实验目的及要求

二、实验内容

三、实验设备与环境

四、实验设计方案

(一)实验步骤:

(二)设计思想:

(三)算法描述或开发流程:

五、实验结果

 六、附录源码


一、实验目的及要求

1.理解二叉树的数据结构及其在解决实际问题中的应用。
2.掌握二叉树的查找操作,包括前序、中序和后序遍历。
3.实现家谱关系的二叉树表示,能够有效地存储和查找家谱信息。
4.实现文件操作功能,将家谱信息保存到文件中,并能从文件中读取。
5.实现家谱操作功能,包括添加、删除和修改家谱记录。

二、实验内容

用二叉树表示家谱关系并实现各种查找功能。要求编写一个程序,采用一棵二叉树表示一个家谱关系(由若干家谱记录构成,每个家谱记录由父亲、妻子和儿子的姓名构成,姓名是关键字)要求程序具有文件操作功能和家谱操作功能。

三、实验设备与环境

1.Windows11

2.Codeblocks

四、实验设计方案

(一)实验步骤:

1.定义二叉树的数据结构,包括节点和二叉树本身。节点应包含姓名、指向父亲的指针、指向妻子的指针和指向儿子的指针。
2.实现二叉树的查找功能,包括前序、中序和后序遍历。前序遍历先访问节点,然后遍历左子树,最后遍历右子树;中序遍历先遍历左子树,然后访问节点,最后遍历右子树;后序遍历先遍历左子树,然后遍历右子树,最后访问节点。
3.实现家谱关系的二叉树表示,将每个家谱记录转换为二叉树中的一个节点。根据家谱记录中的父亲、妻子和儿子的姓名作为关键字进行查找。
4.实现文件操作功能,将家谱信息保存到文件中,并能从文件中读取。可以使用序列化和反序列化的方法将二叉树转换为文件中的数据结构,以及从文件中读取数据并重构二叉树。
5.实现家谱操作功能,包括添加、删除和修改家谱记录。添加操作可以通过递归地查找目标节点并创建新的子节点来实现;删除操作需要找到目标节点并删除它;修改操作需要找到目标节点并更新它的信息。

(二)设计思想:

1.使用二叉树表示家谱关系可以有效地存储和查找家谱信息。由于二叉树具有较快的查找速度,因此可以快速地找到目标节点。
2.使用文件操作功能可以将家谱信息持久化存储,方便备份和迁移数据。同时,也可以从文件中读取数据并重构二叉树,实现数据的恢复和加载。
3.家谱操作功能是本实验的核心部分,需要实现添加、删除和修改家谱记录的操作。这些操作需要遵循一定的逻辑规则,例如在删除操作中需要考虑节点的父子关系和平衡性等。

(三)算法描述或开发流程:

1.定义二叉树的数据结构:使用结构体定义节点类型,包含姓名、指向父亲的指针、指向妻子的指针和指向儿子的指针等字段。同时定义二叉树类型,包含根节点和其他节点等字段。
2.实现二叉树的查找功能:根据前序、中序和后序遍历的定义,编写相应的递归函数来实现查找操作。查找函数应接收一个节点作为参数,并返回该节点的信息或空指针(表示未找到)。
3.实现家谱关系的二叉树表示:编写一个函数将家谱记录转换为二叉树中的一个节点。该函数应接收一个家谱记录作为参数,并根据父亲、妻子和儿子的姓名在二叉树中进行查找或创建新的节点。
4.实现文件操作功能:编写序列化和反序列化的函数,将二叉树转换为文件中的数据结构,以及从文件中读取数据并重构二叉树。序列化函数应接收一个节点作为参数,并将其写入文件中;反序列化函数应从文件中读取数据并创建一个新的节点或子树。
5.实现家谱操作功能:编写添加、删除和修改家谱记录的函数。添加函数应接收一个家谱记录作为参数,并在二叉树中创建新的节点;删除函数应接收一个姓名作为参数,并在二叉树中查找并删除相应的节点;修改函数应接收一个家谱记录作为参数,并在二叉树中查找并更新相应的节点信息。

五、实验结果

 六、附录源码

#include <stdio.h>
#include <malloc.h>
#include <string.h>

#define MAX_SIZE (30)           // 栈的最大元素个数
#define NAME_WIDTH (10)         // 姓名的最多字符个数

typedef struct fnode
{
    char father[NAME_WIDTH];    // 父亲姓名
    char wife[NAME_WIDTH];      // 母亲姓名
    char son[NAME_WIDTH];       // 儿子姓名
}fam_type;                      // 家谱文件的记录类型

typedef struct tnode
{
    char name[NAME_WIDTH];      // 姓名
    struct tnode *lchild;       // 左孩子指针
    struct tnode *rchild;       // 右孩子指针
}btree;                         // 家谱二叉树结点类型

static int n;                          // 家谱记录个数
static fam_type fam[MAX_SIZE];         // 家谱记录数组

/*---------------------家谱二叉树操作算法------------------*/
/*---------------------从fam(含n个记录)递归创建一颗二叉树------------------*/
static btree *create_btree(char *root)
{
    btree *b;
    btree *p;
    int i = 0;
    int j;

    b = (btree *)malloc(sizeof(btree));                     // 创建父亲结点
    strcpy(b->name, root);
    b->lchild = b->rchild = NULL;
    while(i < n && strcmp(fam[i].father, root) != 0)
    {
        i++;
    }

    if(i < n)                                               // 找到该姓名的记录
    {
        p = (btree *)malloc(sizeof(btree));
        p->lchild = p->rchild = NULL;
        strcpy(p->name, fam[i].wife);
        b->lchild = p;
        for(j = 0; j < n; j++)                              // 找所有儿子
        {
            if(strcmp(fam[j].father, root) == 0)            // 找到一个儿子
            {
                p->rchild = create_btree(fam[j].son);
                p = p->rchild;
            }
        }
    }

    return b;
}

/*--------------------------以括号表示法输出二叉树----------------------*/
static void disp_btree(btree *b)
{
    if(b != NULL)
    {
        printf("%c", b->name);
        if(b->lchild != NULL || b->rchild != NULL)
        {
            printf("("); // 有孩子结点时才输出(
            disp_btree(b->lchild); // 递归处理左子树
            if(b->rchild != NULL) // 有右孩子结点时才输出,
                printf(",");
            disp_btree(b->rchild); // 递归处理右子树
            printf(")"); // 有孩子结点时才输出)
        }
    }
}

/*--------------------------采用先序递归算法查找name为xm的结点----------------------*/
static btree *find_node(btree *b, char xm[])
{
    btree *p;

    if(b == NULL)
        return NULL;
    else
    {
        if(strcmp(b->name, xm) == 0)
            return b;
        else
        {
            p = find_node(b->lchild, xm);                   // 递归处理左子树
            if(p != NULL)
                return p;
            else
                return find_node(b->rchild, xm);            // 递归处理右子树
        }
    }
}

/*--------------------------输出某人的所有儿子----------------------*/
static void find_son(btree *b)
{
    char xm[NAME_WIDTH];
    btree *p;

    printf("  >>父亲姓名:");
    scanf("%s", xm);
    p = find_node(b, xm);
    if(p == NULL)
        printf("  >>不存在%s的父亲!\n", xm);
    else
    {
        p = b->lchild;
        if(p == NULL)
            printf("  >>%s没有妻子\n", xm);
        else
        {
            p = p->rchild;
            if(p == NULL)
                printf("  >>%s没有儿子!\n", xm);
            else
            {
                printf("  >>%s的儿子\n", xm);
                while(p != NULL)
                {
                    printf("%10s", p->name);
                    p = p->rchild;
                }
                printf("\n");
            }
        }
    }
}

/*---------------------采用后序非递归遍历算法输出从根结点到s结点的路径------------------*/
static int path(btree *b, btree *s)
{
    btree *st[MAX_SIZE];                            // 定义顺序栈
    btree *p;
    int top = -1;                                   // 栈指针设置初值
    int i;
    bool flag;

    do
    {
        while(b)                                    // 将b的所有左下结点进栈
        {
            top++;
            st[top] = b;
            b = b->lchild;
        }
        p = NULL;                                   // p指向当前结点的前一个已访问的结点
        flag = true;                                // flag为真表示正在处理栈顶结点
        while(top != -1 && flag)
        {
            b = st[top];                            // 取出当前的栈顶元素
            if(b->rchild == p)                      // 右子树不存在或已被访问,访问之
            {
                if(b == s)                          // 当前访问的结点为要找的结点,输出路径
                {
                    printf("   >>所有祖先:");
                    for(i = 0; i < top; i++)
                        printf("%s ", st[i]->name);
                    printf("\n");
                    return 1;
                }
                else
                {
                    top--;
                    p = b;                          // p指向被访问的结点
                }
            }
            else
            {
                b = b->rchild;                      // b指向右子树
                flag = false;                       // 表示当前不是处理栈顶结点
            }
        }
    }while(top > -1);

    return 0;                                       // 其他情况时返回0
}

/*---------------------输出某人的所有祖先------------------*/
static void ancestor(btree *b)
{
    btree *p;
    char xm[NAME_WIDTH];

    printf("  >>输入姓名:");
    scanf("%s", xm);
    p = find_node(b, xm);
    if(p != NULL)
        path(b, p);
    else
        printf("  >>不存在%s\n", xm);
}

/*---------------------销毁家谱二叉树------------------*/
static void destroy_btree(btree *b)
{
    if(b != NULL)
    {
        destroy_btree(b->lchild);
        destroy_btree(b->rchild);
        free(b);
    }
}

/*---------------------家谱文件操作算法------------------*/
/*---------------------清除家谱文件全部记录------------------*/
static void del_all(void)
{
    FILE *fp = NULL;

    fp = fopen("fam.dat", "wb");
    if(fp == NULL)
    {
        printf("  >>不能打开家谱文件\n");
        return;
    }
    n = 0;

    fclose(fp);
}

/*---------------------读家谱文件存入fam数组------------------*/
static void read_file(void)
{
    FILE *fp = NULL;            // 文件指针
    long len;                   // 家谱文件长度
    int n;                      // 家谱文件中的记录个数

    fp = fopen("fam.dat", "rb");
    if(fp == NULL)
    {
        n = 0;
        return;
    }
    fseek(fp, 0, SEEK_END);    // 家谱文件位置指针移到家谱文件末尾
    len = ftell(fp);           // len求出家谱文件长度
    rewind(fp);                // 家谱文件位置指针移到家谱文件开头
    n = len / sizeof(fam_type);// n求出家谱文件中的记录个数
    for(int i = 0; i < n; i++)
        fread(&fam[i], sizeof(fam_type), 1, fp); // 将家谱文件中的数据读到fam数组中
    fclose(fp);
}

/*---------------------添加一个记录------------------*/
static void input_fam(void)
{
    printf("    >>输入父亲、母亲和儿子姓名:");
    scanf("%s %s %s", fam[n].father, fam[n].wife, fam[n].son);
    n++;
}

/*---------------------输出家谱文件全部记录------------------*/
static void output_file(void)
{
    int i;

    if(n <= 0)
    {
        printf("  >>没有任何记录\n");
        return;
    }

    printf("            父亲       母亲      儿子\n");
    printf("         ------------------------------------\n");
    for(i = 0; i < n; i++)
        printf("    %10s %10s %10s\n", fam[i].father, fam[i].wife, fam[i].son);
    printf("         ------------------------------------\n");
}

/*---------------------将fam数组存入数据家谱文件------------------*/
static void save_file(void)
{
    int i;
    FILE *fp = NULL;

    fp = fopen("fam.dat", "wb");
    if(fp == NULL)
    {
        printf("  >>数据家谱文件不能打开\n");
        return;
    }

    for(i = 0; i < n; i++)
    {
        fwrite(&fam[i], sizeof(fam_type), 1, fp);
    }
    fclose(fp);
}

/*---------------------家谱文件操作------------------*/
static void file_operation(void)
{
    int sel;

    do
    {
        printf(" >1:输入 2:输出 9:全清 0:存盘返回 请选择:");
        scanf("%d", &sel);
        switch(sel)
        {
        case 1:
            input_fam();
            break;
        case 2:
            output_file();
            break;
        case 9:
            del_all();
            break;
        case 0:
            save_file();
            break;
        }
    }while(sel != 0);
}

/*---------------------家谱二叉树操作------------------*/
static void btree_operation(void)
{
    btree *b;
    int sel;

    if(n == 0)                           // 家谱记录为0时直接返回
        return;
    b = create_btree(fam[0].father);
    do
    {
        printf(" >1:以括号表示法输出二叉树 2:找某人所有儿子 3:找某人所有祖先 0:返回 请选择:");
        scanf("%d", &sel);
        switch(sel)
        {
        case 1:
            printf("  >>");
            disp_btree(b);
            printf("\n");
            break;
        case 2:
            find_son(b);
            break;
        case 3:
            printf("  >>");
            ancestor(b);
            break;
        }
    }while(sel != 0);

    destroy_btree(b);                    // 销毁家谱二叉树
}

int main(int argc, char *argv[])
{
    btree *b;
    int sel;

    read_file();
    do
    {
        printf("*1:文件操作 2:家谱操作 0:退出 请选择:");
        scanf("%d", &sel);
        switch(sel)
        {
        case 1:
            file_operation();
            break;
        case 2:
            btree_operation();
            break;
        }
    }while(sel != 0);
    return 0;
}

 

  • 28
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
家谱管理系统,主要用来管理家族成员的基本信息 1、确定整个程序的功能模块。实现程序的主界面,要对主界面的功能选择输入进行容错处理。 2、实现单个结点信息的录入。 3、对录入日期信息进行合法性检验。 4、采用改变字体颜色的方式突出显示主界面的功能项。 5、计算从出生日期到死亡日期的实际天数 6、若家谱树为空,则新建家谱树。实现成员节点的添加。基本功能中可以 强制要求所有成员不同名,即不考虑同名情况(符合小家族的实际情况)。 7、添加成员节点时,可以选择将新添加的节点作为整个家谱的上一代祖先, 或者将新添加的节点作为某个现有成员的孩子。 8、作为某个现有成员的孩子,根据给出的父节点的姓名将该结点添加到相 应位置,注意,针对某一父节点,添加第一个孩子和其它孩子的区别。 9、要求在孩子兄弟二叉树中按各个孩子的年龄进行排序。 10、将家谱树保存到进制文件。注意,不能保存空白节点。 11、从文件读入家谱信息,重建孩子兄弟二叉树形式的家谱。 12.从文件中读出所有节点信息到一个数组中,然后按一年中生日的先后进 行快速排序。 13、按姓名查询家谱成员并显示该成员的各项信息。 14、给出某一成员的姓名,删除该成员和该成员的所有子孙。 15、成员信息的修改。信息修改时要给出选择界面让用户选择需要修改的信 息项。基本功能中可以限定不容许修改父亲姓名和本人姓名。对日期信 息进行修改时要进行检验。 16、实现层次递进的方式显示整个家谱,显示结果应该体现家谱树的结构。 17、按各种关键字进行查询,要求给出关键字选择界面,并显示符合查询条 件的节点信息。 18、信息统计基本要求包括:平均身高,平均寿命,男女成员各多少,平均 家庭人口数目(假定每个成员构成一个家庭,该家庭的家庭成员是指成 员本人和他的孩子,即家庭人口数=孩子数+1)。要给出统计项的选择界 面. 19、查询某一成员的所有直系亲属。 20、给出某一成员的所有嫡系祖先。 21、确定两人关系。若两人辈分不等,则应指出甲是乙的多少代长辈(晚辈), 甲是否是乙的直系长辈(晚辈),若辈分相同,则应指出是亲兄弟还是多 少代的堂兄弟。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

噗-噗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值