数据结构大作业——家谱管理系统(超详细!完整代码!)

news2025/1/13 9:04:18

目录

设计思路:

一、项目背景

二、功能分析

查询功能流程图: 

管理功能流程图: 

三、设计

四、实现

代码实现:

头文件

结构体

函数声明及定义

创建家谱树头结点

绘制家谱树(打印)

建立右兄弟

建立左孩子

建立孩子结点信息

查找x的孩子

查找祖先

添加孩子

添加兄弟

中序遍历

删除结点

主函数

完整代码:

调试分析及测试结果:

进入主界面

建立家谱

生成树

查询操作

删除操作

写在最后


设计思路:

一、项目背景

家谱是一种以表谱形式,记载一个以血缘关系为主体的家族世袭繁衍和重要人物事迹的特殊图书体裁。家谱是中国特有的文化遗产,是中华民族的三大文献(国史,地志,族谱)之一,属于珍贵的人文资料,对于历史学,民俗学,人口学,社会学和经济学的深入研究,均有其不可替代的独特功能。

经历了历朝历代的连年战乱和社会动荡,历史上传世的家谱几乎丧失殆尽,许多家族的世系也因此断了线、失了传。流传至今的古代家谱,大多是明清两代纂修。在我国明清时期,出现了专门替人伪造家谱世系的“谱匠”。 

本项目旨在完成一个家谱系统,并实现家谱树所需要的查找、插入、搜索和删除等相关功能。


二、功能分析

完成一个简易的家谱管理系统,主要包含了管理和查询两大功能。

首先允许用户进行家谱的创建并能简易的输出整个家谱。其次,还要具有查询某结点祖先和孩子的功能,同时为保证用户可以随时修改家谱,添加了完善孩子、完善兄弟和删除结点的功能。其中删除结点规则定义为:若有孩子,则孩子一并删去;若有兄弟,则保留兄弟。最后考虑到现实中用户中输入错误的情况,还要包括健壮性的检查。

查询功能流程图: 

管理功能流程图: 


三、设计

该程序具有明显的树形结构,故采用树作为数据结构。我们选择采用二叉树,每个结点包括三个域,具体为lchild,rbrother,data,分别用来存储孩子、兄弟和此结点的名称。

结点代码设计如下:

typedef struct Node

{

string data;

struct Node* lchild;//左孩子

struct Node* rbrother;//右兄弟

}SLNode;

void Initiate(SLNode** T)这个函数用来创建家谱树头结点

SLNode* Insertright(SLNode* arr, string x)这个函数用来建立右兄弟

SLNode* Insertleft(SLNode* arr, string x)这个函数用来建立左孩子

void input(SLNode* arr)这个函数用来输入结点的儿子的信息

void PrintTree(SLNode* T, int n)这个函数用来打印家谱树

void Searchchild(SLNode* T, string x,bool &flag)这个函数用来查找x的孩子,在树T中查找x是否存在,并用flag来标记

void SearchAncestor(SLNode* T, string x, bool& flag)这个函数用来查找x的祖先,在树T中查找x是否存在,并用flag来标记

void addChild(SLNode* T, string x, string t)这个函数用来添加孩子,在树T中找到x结点并把t加入其左孩子之中

void addBrother(SLNode* T, string x, string t)这个函数用来添加兄弟,在树T中找到x结点并把t加入其左孩子之中

void inOrder(SLNode* T, string x, bool& flag)这个函数中序遍历查找x是否存在,并用flag来标记

void deleteNode(SLNode* T, string x)这个函数用来解散结点x的家庭


四、实现

完善家谱功能流程图


代码实现:

头文件

#include<iostream>
#include<windows.h>
#include <stdio.h>
#include <stdlib.h>
#include<string>

结构体

typedef struct Node
{
    string data;
    struct Node* lchild;//左孩子 
    struct Node* rbrother;//右兄弟 
}SLNode;

函数声明及定义

void Initiate(SLNode** T);
SLNode* Insertright(SLNode* arr, string x);
SLNode* Insertleft(SLNode* arr, string x);
void input(SLNode* arr);
void PrintTree(SLNode* T, int n);
void Searchchild(SLNode* T, string x);
void SearchAncestor(SLNode* T, string x);
void addChild(SLNode* T, string x, string t);
void addBrother(SLNode* T, string x, string t);
void inOrder(SLNode* T, string x, bool& flag);
void deleteNode(SLNode* T, string x);
SLNode* p;

创建家谱树头结点

void Initiate(SLNode** T)
{
    *T = new SLNode;
    (*T)->lchild = NULL;
    (*T)->rbrother = NULL;
}

绘制家谱树(打印)

void PrintTree(SLNode* T, int n)
{
    int i, j;
    if (T)
    {
        for (i = 0; i < n; i++) cout << "       " ;
        cout << T->data;
        cout << endl;
        //打印家谱时为左孩子n+1,整体向右推移一位,右兄弟依然是n
        PrintTree(T->lchild, n + 1);
        PrintTree(T->rbrother, n);
    }
}

建立右兄弟

SLNode* Insertright(SLNode* arr, string x)
{
    SLNode* m;
    if (arr == NULL) return NULL;
    if (arr->rbrother != NULL)  arr = arr->rbrother;
    m = new SLNode;
    m->data = x;
    m->rbrother = arr->rbrother;
    m->lchild = NULL;
    arr->rbrother = m;
    return arr->rbrother;
}

建立左孩子

SLNode* Insertleft(SLNode* arr, string x)
{
    SLNode* m;
    if (arr == NULL) return NULL;
    else if (arr->lchild == NULL)
        //为什么这里要判空?
        //因为有可能插入多个孩子,如果已经插入一个或多个了,就需要执行else块里的右兄弟函数往下递归找到空指针在插入 
    {
        //开始创建要插入的左孩子 
        m = new SLNode;//malloc无法为string分配正确内存,所以用new
        m->data = x;
        m->lchild = arr->lchild;//方便下次插入孩子结点 
        m->rbrother = NULL;

        arr->lchild = m;//孩子结点插入完成 
        return arr->lchild;
    }
    else
    {
        Insertright(arr->lchild, x);
    }
}

建立孩子结点信息

void input(SLNode* arr)
{
    string p;
    if (arr == NULL) return;
    cout << "请输入"<<arr->data<<"所有的儿子结点, 若没有儿子或者输完所有儿子,输入#即可:" << endl;
    cin >> p;
    while (p != "#")
    {
        Insertleft(arr, p);
        cin >> p;
    }
    //这个建立家谱的过程,为了防止输入混乱,先递归兄弟结点,再递归孩子结点 
    if (arr->rbrother != NULL)
        input(arr->rbrother);
    if (arr->lchild != NULL)
        input(arr->lchild);
    /*
    if (arr->rbrother != NULL )
        input(arr->rbrother);
    if (arr->lchild != NULL )
        input(arr->lchild);
 }

查找x的孩子

void Searchchild(SLNode* T, string x,bool &flag)
{
    SLNode* p;
    //要用T->data和x比较,所以要保证T不为空指针
    if (T != NULL && T->data != x)
    {
        Searchchild(T->lchild, x,flag);
        Searchchild(T->rbrother, x,flag);
    }
    //加入限定条件只允许T->data==x时通过
    if (T != NULL && T->lchild != NULL && T->data == x)
    {
        cout << T->data << "结点的儿子结点为:" << T->lchild->data << endl;
        p = T->lchild;
        while (p->rbrother != NULL)//右兄弟,找到一个儿子结点后去右子树找兄弟结点
        {
            cout << p->rbrother->data << endl;
            p = p->rbrother;
        }
    }
}



查找祖先

void SearchAncestor(SLNode* T, string x, bool& flag)//函数为找x的祖先 
{
    if (T != NULL && T->data != x)//如果T不为空并且data不是x 
    {
        SearchAncestor(T->lchild, x, flag);//不断向下递归找左孩子 
        SearchAncestor(T->rbrother, x, flag);//右兄弟 
    }
    //保证p为不变的指针,指向选择的结点
    if (T != NULL && T->data == x)//T不为空并且已经找到了结点 
    {
        p = T;//此时的结点赋给p 
        flag = true;//说明已经找到了 
    }
    //下面找p的祖先即可 ,T是p的祖先,可能T的左孩子就是了也可能是T的左孩子的兄弟 
    if (T != NULL && T->lchild != NULL && (T->lchild == p || T->lchild->rbrother == p))	
    {
        cout << "他的祖先结点为:" << T->data << endl;
        p = T;
    }
}

添加孩子

void addChild(SLNode* T, string x, string t)
{
    //考虑到现实生活中会有二胎三胎,这个函数的作用即为添加孩子添加孩子结点 
    if (T != NULL && T->data != x)
    {
        addChild(T->lchild, x, t);
        addChild(T->rbrother, x, t);
    }
    if (T != NULL && T->data == x)
    {
        p = T;
        Insertleft(p, t);//调用前面的插入左孩子函数进行添加 
    }
}

添加兄弟

void addBrother(SLNode* T, string x, string t)
{
    if (T != NULL && T->data != x)
    {

        addBrother(T->rbrother, x, t); addBrother(T->lchild, x, t);
    }
    if (T != NULL && T->data == x)
    {
        p = T;
        Insertright(p, t);
        return;
    }
}

中序遍历

void inOrder(SLNode* T, string x, bool& flag)//中序遍历
{
    if (T == NULL || flag == true) return;
    inOrder(T->lchild, x, flag);
    if (T->data == x)
    {
        flag = true;//如果要完善的结点在家谱里,flag为真 
        return;
    }
    inOrder(T->rbrother, x, flag);
}

删除结点

void deleteNode(SLNode* T, string x)
{
    //先定义删除规则:
    //如果有孩子,孩子一并删去,有兄弟则保留兄弟。
    if (T == NULL) return;
    //因为T为头结点,这里从T->lchild开始遍历
    if ( T->lchild != NULL && T->lchild->data == x)
    {
        SLNode* p = T->lchild->lchild;
        free(p);//根据删除规则,孩子一并删除
        T->lchild = T->lchild->rbrother;//保留兄弟
    }
    if (T->rbrother != NULL && T->rbrother->data == x)
    {
        SLNode* p = T->rbrother->lchild;
        free(p);//根据删除规则,孩子一并删除
        T->rbrother = T->rbrother->rbrother;//保留兄弟
    }
    deleteNode(T->lchild, x);
    deleteNode(T->rbrother, x);
    //先根再左在右,先序遍历的方式删除
}

主函数

int main()
{
    SLNode* T;
    string p;
    int n;
    Initiate(&T);
    do
    {
        system("color 75");
        cout << "                                                           " << endl;
        cout << "                        家谱管理系统                    " << endl;
        cout << "------------------------- 功能选项 -------------------------";
        cout << endl << endl;
        cout << "                    **  1-开始建立家谱  **" << endl;
        cout << "                    **  2-查询-家谱树   **" << endl;
        cout << "                    **  3-查询-儿子     **" << endl;
        cout << "                    **  4-查询-祖先     **" << endl;
        cout << "                    **  5-完善-孩子     **" << endl;
        cout << "                    **  6-完善-兄弟     **" << endl;
        cout << "                    **  7-删除-结点     **" << endl;
        cout << "                    **  0-退出系统      **" << endl;
        cout << endl << endl;
        cout << "------------------------------------------------------------";
        cout << endl;
        cout << "请选择需要的功能(数字) :";
        char ch;
        ch = getchar();
        switch (ch)
        {
        case '1':
        {
            cout << "请输入祖先结点:" << endl;
            cin >> p;
            Insertleft(T, p);
            input(T->lchild); getchar(); break;
        };
        case '2':
        {
            cout << endl;
            PrintTree(T->lchild, 1); getchar(); break;
        };
        case '3':
        {
            bool flag_1 = false;//flag_1用来标记是否有孩子
            bool flag_2 = false;//flag_2用来标记家谱里是否有要查询的结点
            cout << "请输入要查询的结点" << endl;
            cin >> p;
            inOrder(T, p, flag_2);
            //inOrder(T, p, flag_1); 
            while (!flag_2)
            {
                cout << "您要查询的结点并不存在, 请重新输入:" << endl;
                cin >> p;
                inOrder(T, p, flag_2);
            }
            Searchchild(T, p,flag_1); //直接在此函数中输出
            getchar(); break;
        };
        case '4':
        {
            cout << "请输入要查询的结点:" << endl;
            cin >> p;
            bool flag = false;
            while (p == T->lchild->data)//T是头结点,祖先存储在T的lchild域里
            {
                cout << "这个结点为祖先,请重新输入:" << endl;
                cin >> p;
            }
            SearchAncestor(T->lchild, p, flag);
            while (flag == false)
            {
                cout << "此结点不存在,请重新输入:" << endl;
                cin >> p;
                while (p == T->lchild->data)//T是头结点,祖先存储在T的lchild域里
                {
                    cout << "这个结点为祖先,请重新输入:" << endl;
                    cin >> p;
                }
                SearchAncestor(T->lchild, p, flag);
            }
            getchar(); break;
        };
        case '5':
        {
            bool flag = false;//flag用来标记要完善的结点是否在家谱里 
            cout << "请输入要完善的结点:" << endl;
            cin >> p;
            inOrder(T, p, flag);//查找结点是否在家谱里
            while (!flag)
            {
                cout << "您要完善的结点并不存在, 请重新输入:" << endl;
                cin >> p;
                inOrder(T, p, flag);
            }
            cout << "要添加的孩子为:" << endl;
            string x;
            cin >> x;
            addChild(T, p, x); getchar(); break;
        };
        case '6':
        {
            bool flag = false;//flag用来标记要完善的结点是否在家谱里
            cout << "请输入要完善的结点:" << endl;
            cin >> p;
          
            inOrder(T, p, flag);//查找结点是否在家谱里
            while (!flag)
            {
                cout << "您要完善的结点并不存在,请重新输入:" << endl;
                cin >> p;
                inOrder(T, p, flag);
            }
            cout << "要添加的兄弟为:" << endl;
            string x;
            cin >> x;
            addBrother(T, p, x); getchar(); break;
        }
        case '7':
        {
            bool flag = false;//标记要删除的结点是否在家谱里
            cout << "请输入要删除的结点:" << endl;
            cin >> p;
            inOrder(T, p, flag);//查找结点是否在家谱里
            while (!flag)
            {
                cout << "您要删除的结点并不存在,请重新输入:" << endl;
                cin >> p;
                inOrder(T, p, flag);
            }
            deleteNode(T, p); getchar(); break;
        }
        case '0':
        {
            cout << " 感谢您的使用,下次再见!" << endl;
            exit(0);
        }
        default:
        {
            cout << "输入有误,请重新输入:" << endl;
            ch = getchar();
        }
        }
    } while (1);
    return 0;
}

完整代码:

#include<iostream>
#include<windows.h>
#include <stdio.h>
#include <stdlib.h>
#include<string>

using namespace std;

typedef struct Node
{
    string data;
    struct Node* lchild;//左孩子 
    struct Node* rbrother;//右兄弟 
}SLNode;

//extern SLNode* p;

void Initiate(SLNode** T);
SLNode* Insertright(SLNode* arr, string x);
SLNode* Insertleft(SLNode* arr, string x);
void input(SLNode* arr);
void PrintTree(SLNode* T, int n);
void Searchchild(SLNode* T, string x);
void SearchAncestor(SLNode* T, string x);
SLNode* p;

void Initiate(SLNode** T)
{
    *T = new SLNode;
    (*T)->lchild = NULL;
    (*T)->rbrother = NULL;
}

SLNode* Insertright(SLNode* arr, string x)
{
    SLNode* m;
    if (arr == NULL) return NULL;
    if (arr->rbrother != NULL)  arr = arr->rbrother;
    m = new SLNode;
    m->data = x;
    m->rbrother = arr->rbrother;
    m->lchild = NULL;
    arr->rbrother = m;
    return arr->rbrother;
}

SLNode* Insertleft(SLNode* arr, string x)
{
    SLNode* m;
    if (arr == NULL) return NULL;
    else if (arr->lchild == NULL)
        //为什么这里要判空?
        //因为有可能插入多个孩子,如果已经插入一个或多个了,就需要执行else块里的右兄弟函数往下递归找到空指针在插入 
    {
        //开始创建要插入的左孩子 
        m = new SLNode;//malloc无法为string分配正确内存,所以用new
        m->data = x;
        m->lchild = arr->lchild;//方便下次插入孩子结点 
        m->rbrother = NULL;

        arr->lchild = m;//孩子结点插入完成 
        return arr->lchild;
    }
    else
    {
        Insertright(arr->lchild, x);
    }
}

void input(SLNode* arr)
{
    string p;
    if (arr == NULL) return;
    cout << "请输入"<<arr->data<<"所有的儿子结点, 若没有儿子或者输完所有儿子,输入#即可:" << endl;
    cin >> p;
    while (p != "#")
    {
        Insertleft(arr, p);
        cin >> p;

    }
    //这个建立家谱的过程,为了防止输入混乱,先递归兄弟结点,再递归孩子结点 
    if (arr->rbrother != NULL)
        input(arr->rbrother);
    if (arr->lchild != NULL)
        input(arr->lchild);
    /*
    if (arr->rbrother != NULL )
        input(arr->rbrother);
    if (arr->lchild != NULL )
        input(arr->lchild);
    //这里是先递归兄弟节点,再递归孩子结点
    */
}

void PrintTree(SLNode* T, int n)
{
    int i, j;
    if (T)
    {
        for (i = 0; i < n; i++) cout << "       " ;
        cout << T->data;
        cout << endl;
        //打印家谱时为左孩子n+1,整体向右推移一位,右兄弟依然是n
        PrintTree(T->lchild, n + 1);
        PrintTree(T->rbrother, n);
    }
}

void Searchchild(SLNode* T, string x,bool &flag)//flag用来标记是否有孩子
{
    SLNode* p;
    //要用T->data和x比较,所以要保证T不为空指针
    if (T != NULL && T->data != x)
    {
        Searchchild(T->lchild, x,flag);
        Searchchild(T->rbrother, x,flag);
    }
    //加入限定条件只允许T->data==x时通过
    if (T != NULL && T->lchild != NULL && T->data == x)
    {
        cout << T->data << "结点的儿子结点为:" << T->lchild->data << endl;
        p = T->lchild;
        while (p->rbrother != NULL)//右兄弟,找到一个儿子结点后去右子树找兄弟结点
        {
            cout << p->rbrother->data << endl;
            p = p->rbrother;
        }
    }
}

void SearchAncestor(SLNode* T, string x, bool& flag)//函数为找x的祖先 
{
    if (T != NULL && T->data != x)//如果T不为空并且data不是x 
    {
        SearchAncestor(T->lchild, x, flag);//不断向下递归找左孩子 
        SearchAncestor(T->rbrother, x, flag);//右兄弟 
    }
    //保证p为不变的指针,指向选择的结点
    if (T != NULL && T->data == x)//T不为空并且已经找到了结点 
    {
        p = T;//此时的结点赋给p 
        flag = true;//说明已经找到了 
    }
    //下面找p的祖先即可 ,T是p的祖先,可能T的左孩子就是了也可能是T的左孩子的兄弟 
    if (T != NULL && T->lchild != NULL && (T->lchild == p || T->lchild->rbrother == p))
	
    {

        cout << "他的祖先结点为:" << T->data << endl;
        p = T;
    }
}

void addChild(SLNode* T, string x, string t)
{
    //考虑到现实生活中会有二胎三胎,这个函数的作用即为添加孩子添加孩子结点 
    if (T != NULL && T->data != x)
    {
        addChild(T->lchild, x, t);
        addChild(T->rbrother, x, t);
    }
    if (T != NULL && T->data == x)
    {
        p = T;
        Insertleft(p, t);//调用前面的插入左孩子函数进行添加 
    }
}
void addBrother(SLNode* T, string x, string t)
{
    if (T != NULL && T->data != x)
    {

        addBrother(T->rbrother, x, t); addBrother(T->lchild, x, t);
    }
    if (T != NULL && T->data == x)
    {
        p = T;
        Insertright(p, t);
        return;
    }
}
void inOrder(SLNode* T, string x, bool& flag)//中序遍历
{
    if (T == NULL || flag == true) return;
    inOrder(T->lchild, x, flag);
    if (T->data == x)
    {
        flag = true;//如果要完善的结点在家谱里,flag为真 
        return;
    }
    inOrder(T->rbrother, x, flag);
}
void deleteNode(SLNode* T, string x)
{
    //先定义删除规则:
    //如果有孩子,孩子一并删去,有兄弟则保留兄弟。
    if (T == NULL) return;
    //因为T为头结点,这里从T->lchild开始遍历
    if ( T->lchild != NULL && T->lchild->data == x)
    {
        SLNode* p = T->lchild->lchild;
        free(p);//根据删除规则,孩子一并删除
        T->lchild = T->lchild->rbrother;//保留兄弟
    }
    if (T->rbrother != NULL && T->rbrother->data == x)
    {
        SLNode* p = T->rbrother->lchild;
        free(p);//根据删除规则,孩子一并删除
        T->rbrother = T->rbrother->rbrother;//保留兄弟
    }
    deleteNode(T->lchild, x);
    deleteNode(T->rbrother, x);
    //先根再左在右,先序遍历的方式删除
}
int main()
{
    SLNode* T;
    string p;
    int n;
    Initiate(&T);
    do
    {
        system("color 75");
        cout << "                                                           "<<endl;
        cout << "                        家谱管理系统                    "<<endl;
        cout << "------------------------- 功能选项 -------------------------";
        cout << endl << endl;
        cout << "                    **  1-开始建立家谱  **" << endl;
        cout << "                    **  2-查询-家谱树   **" << endl;
        cout << "                    **  3-查询-儿子     **" << endl;
        cout << "                    **  4-查询-祖先     **" << endl;
        cout << "                    **  5-完善-孩子     **" << endl;
        cout << "                    **  6-完善-兄弟     **" << endl;
        cout << "                    **  7-删除-结点     **" << endl;
        cout << "                    **  0-退出系统      **" << endl;
        cout << endl << endl;
        cout << "------------------------------------------------------------";
        cout << endl;
        cout <<"请选择需要的功能(数字) :";
        char ch;
        ch = getchar();
        switch (ch)
        {
        case '1':
        {
            cout << "请输入祖先结点:" << endl;
            cin >> p;
            Insertleft(T, p);
            input(T->lchild); getchar(); break;
        };
        case '2':
        {
            cout << endl;
            PrintTree(T->lchild, 1); getchar(); break;
        };
        case '3':
        {
            bool flag_1 = false;//flag_1用来标记是否有孩子
            bool flag_2 = false;//flag_2用来标记家谱里是否有要查询的结点
            cout << "请输入要查询的结点" << endl;
            cin >> p;
            inOrder(T, p, flag_2);
            while (!flag_2)
            {
                cout << "您要查询的结点并不存在, 请重新输入:" << endl;
                cin >> p;
                inOrder(T, p, flag_2);
            }
            Searchchild(T, p,flag_1); //如果有孩子,则直接在此函数中输出
            if (!flag_1)//flag_1为假,即没有孩子,执行if
            {
                cout << "此结点没有儿子!" << endl;
            }
            getchar(); break;
        };
        case '4':
        {
            cout<< "请输入要查询的结点:" << endl;
            cin >> p;
            bool flag = false;
            while(p == T->lchild->data)//T是头结点,祖先存储在T的lchild域里
            {
                cout << "这个结点为祖先,请重新输入:" << endl;
                cin >> p;
            }
            SearchAncestor(T->lchild, p,flag);
            while (flag == false) 
            {
                cout << "此结点不存在,请重新输入:" << endl;
                cin >> p;
                while(p== T->lchild->data)//T是头结点,祖先存储在T的lchild域里
                {
                    cout << "这个结点为祖先,请重新输入:" << endl;
                    cin >> p;
                }
                SearchAncestor(T->lchild, p, flag);
            }
            getchar();break;
        };
        case '5':
        {
            bool flag = false;//flag用来标记要完善的结点是否在家谱里 
            cout << "请输入要完善的结点:" << endl;
            cin >> p;
            inOrder(T, p, flag);//查找结点是否在家谱里
            while (!flag)
            {
                cout << "您要完善的结点并不存在, 请重新输入:" << endl;
                cin >> p;
                inOrder(T, p, flag);
            }
            cout << "要添加的孩子为:" << endl;
            string x;
            cin >> x;
            addChild(T, p, x); getchar(); break;
        };
        case '6':
        {
            bool flag = false;//flag用来标记要完善的结点是否在家谱里
            cout << "请输入要完善的结点:" << endl;
            cin >> p;
            while (p == T->lchild->data) //T是头结点,祖先存储在T的lchild域里
            {
                cout << "这个结点为祖先,请重新输入:" << endl;
                cin >> p;
            }
            inOrder(T, p, flag);//查找结点是否在家谱里
            while (!flag)
            {
                cout << "您要完善的结点并不存在,请重新输入:" << endl;
                cin >> p;
                inOrder(T, p, flag);
            }
            cout << "要添加的兄弟为:" << endl;
            string x;
            cin >> x;
            addBrother(T, p, x); getchar(); break;
        }
        case '7':
        {
            bool flag = false;//标记要删除的结点是否在家谱里
            cout << "请输入要删除的结点:" << endl;
            cin >> p;
            inOrder(T, p, flag);//查找结点是否在家谱里
            while (!flag)
            {
                cout << "您要删除的结点并不存在,请重新输入:" << endl;
                cin >> p;
                inOrder(T, p, flag);
            }
            deleteNode(T, p); getchar(); break;
        }
        case '0':
        { 
            cout << " 感谢您的使用,下次再见!" << endl;
            exit(0);
        }
        default:
        {
            cout << "输入有误,请重新输入:" << endl;
            ch = getchar();
        }
        }
    } while (1);
    //cout << "hello world!" << endl;
    return 0;
}

调试分析及测试结果:

本项目以《红楼梦》贾府建立家谱树为例

进入主界面


建立家谱


生成树


查询操作


删除操作


写在最后

此次大作业是博主在为完成数据结构课程设计与团队共同完成,此项目比较简单,涉及到的知识点与数据结构都是在树之中扩展的,如果想完成课程设计大作业,此项目分数可以80+(根据学校不同),如果追求更高的成绩,也可以在此项目扩展功能,例如:增加称呼功能(对于任意两个人可以查询出互相称呼什么)、完善个人信息(完善结点结构体,增加年龄、性别、性格等属性)。若你们大作业距离答辩仅剩1-3天,此项目可以用来应急,具体源代码、答辩PPT、测试数据会统一放到一个资源里面,下面会更新链接。也可以私信博主,免费提供给大家。

获取文件可以添加博主vx好友,备注来意,联系我传送门:https://bbs.csdn.net/topics/619404381

最后特此鸣谢团队三人,@池鱼c0de

此篇终,感谢大家支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2275899.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3+elementPlus之后台管理系统(从0到1)(day1)

vue3官方文档&#xff1a;https://cn.vuejs.org/guide/introduction.html 1、项目创建 确保电脑已安装node 查看命令&#xff1a; node -v进入项目目录&#xff0c;创建项目 npm init vuelatest Need to install the following packages: create-vue3.13.0 Ok to procee…

汉图科技XP356DNL高速激光打印一体机综合性能测评

汉图科技XP356DNL高速激光打印一体机效率方面表现出色&#xff0c;支持A4纸型的高速打印&#xff0c;单面打印速度高达35页/分钟&#xff0c;自动双面打印速度可达32面/分钟&#xff0c;这样的速度在日常办公中能够极大地提高打印效率&#xff0c;减少等待时间&#xff0c;满足…

【芯片封测学习专栏 -- 什么是 Chiplet 技术】

请阅读【嵌入式开发学习必备专栏 Cache | MMU | AMBA BUS | CoreSight | Trace32 | CoreLink | ARM GCC | CSH】 文章目录 OverviewChiplet 背景UCIeChiplet 的挑战 Overview Chiplet 又称为小芯片。该技术通过将大型SoC划分为更小的芯片&#xff0c;使得每个部分都能采用不同…

1.CSS的复合选择器

1.1 什么是复合选择器 在CSS中&#xff0c;可以根据选择器的类型把选择器分为基础选择器和复合选择器&#xff0c;复合选择器是建立在基础选择器之上&#xff0c;对基础选择器进行组合形成的。 复合选择器可以更精准、更高效的选择目标元素&#xff08;标签&#xff09; 复…

【MySQL】SQL菜鸟教程(一)

1.常见命令 1.1 总览 命令作用SELECT从数据库中提取数据UPDATE更新数据库中的数据DELETE从数据库中删除数据INSERT INTO向数据库中插入新数据CREATE DATABASE创建新数据库ALTER DATABASE修改数据库CREATE TABLE创建新表ALTER TABLE变更数据表DROP TABLE删除表CREATE INDEX创建…

docker 自建rustdesk服务器测试

参考https://blog.csdn.net/tootsy_you/article/details/130010564 注意&#xff1a; docker-compose.yml version: 3networks:rustdesk-net:external: falseservices:hbbs:container_name: hbbsports:- 21115:21115- 21116:21116- 21116:21116/udp- 21118:21118image: rust…

检验统计量与p值笔记

一、背景 以雨量数据为例&#xff0c;当获得一个站点一年的日雨量数据后&#xff0c;我们需要估计该站点的雨量的概率分布情况&#xff0c;因此我们利用有参估计的方式如极大似然法估计得到了假定该随机变量服从某一分布的参数&#xff0c;从而得到该站点的概率密度函数&#x…

每日十题八股-2025年1月12日

1.为什么四次挥手之后要等2MSL? 2.服务端出现大量的timewait有哪些原因? 3.TCP和UDP区别是什么&#xff1f; 4.TCP为什么可靠传输 5.怎么用udp实现http&#xff1f; 6.tcp粘包怎么解决&#xff1f; 7.TCP的拥塞控制介绍一下&#xff1f; 8.描述一下打开百度首页后发生的网络过…

制造企业“数字化转型”典型场景参考

聚焦产业链上下游企业研发设计、生产制造、运维服务、经营管理、供应链管理等场景&#xff0c;以场景为切入点梳理数字化转型痛点需求&#xff0c;绘制重点行业、重点产业链数字化转型场景图谱&#xff08;简称“一图谱”&#xff09;&#xff0c;明确企业数字化转型路径&#…

Web渗透测试之XSS跨站脚本 防御[WAF]绕过手法

目录 XSS防御绕过汇总 参考这篇文章绕过 XSS payload XSS防御绕过汇总 服务端知道有网络攻击或者xss攻 Html

《机器学习》——sklearn库中CountVectorizer方法(词频矩阵)

CountVectorizer方法介绍 CountVectorizer 是 scikit-learn 库中的一个工具&#xff0c;它主要用于将文本数据转换为词频矩阵&#xff0c;而不是传统意义上的词向量转换&#xff0c;但可以作为词向量转换的一种基础形式。用于将文本数据转换为词频矩阵&#xff0c;它是文本特征…

session-manager-plugin: command not found 解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

Linux之读者写者模型与特殊锁的学习

目录 读者写者模型 特殊锁 悲观锁 自旋锁 在前几期&#xff0c;我们学习了多线程的生产者和消费者模型&#xff0c;生产者和消费者模型中&#xff0c;有三种关系&#xff0c;两个角色&#xff0c;一个场所&#xff0c;那么读者写者模型和生产者消费者模型有什么关联吗&…

期刊(中英),期刊分区,期刊所在数据库(中英),出版商区别和联系

目录 对期刊、分区、数据库、出版商整体了解期刊&#xff08;中英&#xff09;期刊分区期刊所在数据库总结 出版商 对期刊、分区、数据库、出版商整体了解 下图是我对这四部分的一个理解&#xff0c;其中期刊根据论文使用语言分为中英两种&#xff0c;期刊分区是用来评判论文质…

数学函数的参数和返回值探秘

数学函数的参数和返回值探秘 一、数学函数的参数1.1 隐式类型转换1.2 隐式类型转换的秘密 二、数学函数的返回值 本文所说的数学函数单指<math.h>中的系统函数&#xff0c;这些函数对参数和返回值的要求与其他类函数是有一点不同的。尤其是参数部分&#xff0c;是有值得深…

炸砖块游戏的最终图案

描述 小红正在玩一个“炸砖块”游戏,游戏的规则如下:初始有一个 n * m 的砖块矩阵。小红会炸 k 次,每次会向一个位置投炸弹,如果这个位置有一个砖块,则砖块消失,上方的砖块向下落。小红希望你画出最终砖块的图案。 输入描述 第一行输入三个正整数 n, m, k,代表矩阵的行…

代码随想录算法训练营第 4 天(链表 2)| 24. 两两交换链表中的节点19.删除链表的倒数第N个节点 -

一、24. 两两交换链表中的节点 题目&#xff1a;24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; 视频&#xff1a;帮你把链表细节学清楚&#xff01; | LeetCode&#xff1a;24. 两两交换链表中的节点_哔哩哔哩_bilibili 讲解&#xff1a;代码随想录 dummy-…

【微服务】面试题 5、分布式系统理论:CAP 与 BASE 详解

分布式系统理论&#xff1a;CAP 与 BASE 详解 一、CAP 定理 背景与定义&#xff1a;1998 年由加州大学科学家埃里克布鲁尔提出&#xff0c;分布式系统存在一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;、分区容错性&#xff08;Part…

【网络】:网络编程套接字

目录 源IP地址和目的IP地址 源MAC地址和目的MAC地址 源端口号和目的端口号 端口号 VS 进程ID TCP协议和UDP协议 网络字节序 字符串IP和整数IP相互转换 查看当前网络的状态 socket编程接口 socket常见API 创建套接字 绑定端口号 发送数据 接收数据 sockaddr结构…

UnityDemo-TheBrave-制作笔记

这是我跟着b站up主MStudio的视频学习制作的&#xff0c;大体上没有去做一些更新的东西&#xff0c;这里只是一个总的总结。在文章的最后&#xff0c;我会放上可以游玩该游戏的链接和exe可执行文件&#xff0c;不过没有对游戏内容进行什么加工&#xff0c;只有基本的功能实现罢了…