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

目录

设计思路:

一、项目背景

二、功能分析

查询功能流程图: 

管理功能流程图: 

三、设计

四、实现

代码实现:

头文件

结构体

函数声明及定义

创建家谱树头结点

绘制家谱树(打印)

建立右兄弟

建立左孩子

建立孩子结点信息

查找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

此篇终,感谢大家支持。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot参数注解

在Spring Boot中&#xff0c;创建RESTful API时&#xff0c;通常会使用Spring MVC提供的注解来声明请求参数。以下是一些常用的注解及其用途&#xff1a; 1. RequestBody 用途&#xff1a;用于将HTTP请求的body部分绑定到方法参数上&#xff0c;通常用于接收JSON或XML格式的数…

uniapp实现在card卡片组件内为图片添加长按保存、识别二维码等功能

在原card组件的cover属性添加图片的话&#xff0c;无法在图片上面绑定 show-menu-by-longpress"true"属性&#xff0c;通过将图片自定义添加可使用该属性。 代码&#xff1a; <uni-card title"标题" padding"10px 0" :thumbnail"avata…

【Springer斯普林格出版,Ei稳定,往届快速见刊检索】第四届电子信息工程、大数据与计算机技术国际学术会议(EIBDCT 2025)

第四届电子信息工程、大数据与计算机技术国际学术会议&#xff08;EIBDCT 2025&#xff09;将于2025年2月21-23日在中国青岛举行。该会议主要围绕电子信息工程、大数据、计算机技术等研究领域展开讨论。会议旨在为从事相关科研领域的专家学者、工程技术人员、技术研发人员提供一…

docker内外如何实现ROS通信

写在前面 在一台电脑上装有docker&#xff0c;docker内外均装有ROS系统&#xff0c;现在想要实现docker内外的ROS通信&#xff0c;怎么办呢&#xff1f; 首先&#xff0c;因为是同一台电脑的docker内外&#xff0c;所以IP本身是互通的&#xff0c;不需要在/etc/hosts中添加IP…

2025年01月07日Github流行趋势

项目名称&#xff1a;khoj 项目地址url&#xff1a;https://github.com/khoj-ai/khoj项目语言&#xff1a;Python历史star数&#xff1a;20105今日star数&#xff1a;363项目维护者&#xff1a;debanjum, sabaimran, MythicalCow, aam-at, shantanuSakpal项目简介&#xff1a;你…

web3与AI结合-Sahara AI 项目介绍

背景介绍 Sahara AI 于 2023 年创立&#xff0c;是一个 "区块链AI" 领域的项目。其项目愿景是&#xff0c;利用区块链和隐私技术将现有的 AI 商业模式去中心化&#xff0c;打造公平、透明、低门槛的 “协作 AI 经济” 体系&#xff0c;旨在重构新的利益分配机制以及…

patchwork++地面分割学习笔记

参考资料&#xff1a;古月居 - ROS机器人知识分享社区 https://zhuanlan.zhihu.com/p/644297447 patchwork算法一共包含四部分内容&#xff1a;提出了以下四个部分&#xff1a;RNR、RVPF、A-GLE 和 TGR。 1&#xff09;基于 3D LiDAR 反射模型的反射噪声消除 (RNR)&#xff…

渗透测试-非寻常漏洞案例

声明 本文章所分享内容仅用于网络安全技术讨论&#xff0c;切勿用于违法途径&#xff0c;所有渗透都需获取授权&#xff0c;违者后果自行承担&#xff0c;与本号及作者无关&#xff0c;请谨记守法. 此文章不允许未经授权转发至除先知社区以外的其它平台&#xff01;&#xff0…

全局变量(PHP)(小迪网络安全笔记~

免责声明&#xff1a;本文章仅用于交流学习&#xff0c;因文章内容而产生的任何违法&未授权行为&#xff0c;与文章作者无关&#xff01;&#xff01;&#xff01; 附&#xff1a;完整笔记目录~ ps&#xff1a;本人小白&#xff0c;笔记均在个人理解基础上整理&#xff0c;…

耗时一天,我用AI开发了AI小程序

小码哥从事前后端开发近十年&#xff0c;但是随着技术的更新迭代&#xff0c;有时候没有时间和精力去优化UI、实现一些前后端功能&#xff0c;以及解决一些bug。特别是我想开发小码哥AI的移动端&#xff0c;但觉得自己没有那么多时间去研究移动端了&#xff0c;准备放弃了&…

细说STM32F407单片机以轮询方式读写外部SRAM的方法

目录 一、实例的功能 二、工程配置 1、KEYLED 2、时钟、DEBUG、USART6、NVIC、GPIO、CodeGenerator 3、FSMC &#xff08;1&#xff09; 模式设置 &#xff08;2&#xff09; Bank 1子区3参数设置 1) NOR/PSRAM control组&#xff0c;子区控制参数 2) NOR/PSRAM timi…

LLM prompt提示构造案例:语音回复内容;o1思维链

1、语音回复内容 目的&#xff1a; 语音聊天助手的prompt&#xff0c;让大模型来引导聊天内容&#xff0c;简短和友好&#xff0c;从而文字转语音时候也比较高效。 ## 角色设定与交互规则 ### 基本角色 你是用户的好朋友. 你的回答将通过逼真的文字转语音技术阅读. ### 回答规则…

【51单片机零基础-chapter3:按键:独立按键|||附带常见C语句.逻辑运算符】

将unsigned char var0;看作沟通二进制和十进制的桥梁 var是8位,初始为0000 0000; 同时可以进行十进制的运算 逻辑运算 位运算 & 按位与(有0则0) | 按位或(有1则1) ~ 按位非 ^ 按位异或(相同则1,不同为0) <<按位左移 >>按位右移 位运算符解释: 0011 1100 <&…

非一般的小数:小数的概念新解、小数分类、浮点数的存储

非一般的小数&#xff1a;小数的概念新解、小数分类、浮点数的存储 一、小数的概念二、小数的分类1&#xff0e;有限小数、无限循环小数、无限不循环小数2&#xff0e;纯小数、带小数3&#xff0e;定点数、浮点数 三、浮点数的存储 一、小数的概念 这还用解释吗&#xff1f;小…

ETCD渗透利用指南

目录 未指定使用put操作报错 未指定操作版本使用get报错 首先etcd分为两个版本v2和v3&#xff0c;不同的API结果无论是访问URL还是使用etcdctl进行通信&#xff0c;都会导致问题&#xff0c;例如使用etcdctl和v3进行通信&#xff0c;如果没有实名ETCDCTL_API3指定API版本会直接…

小程序组件 —— 28 组件案例 - 推荐商品区域 - 实现结构样式

这一节目标是实现底部推荐商品的结构和样式&#xff0c;由于这里要求横向滚动&#xff0c;所以需要使用上节介绍的 scroll-view 功能&#xff0c;并使用 scroll-x 属性支持横向滚动&#xff0c;推荐商品区域中的每一个商品是一个单独的 view&#xff0c;每个view 中需要写三个组…

JDK、JRE、JVM三者的关系、JDK8的新特性、JVM内存结构,堆栈的区别

1&#xff0e;JDK、JRE、JVM三者的关系 JDK (Java Development Kit)----Java开发工具包&#xff0c;用于Java程序的开发。 JRE (Java Runtime Environment)----Java运行时环境&#xff0c;只能运行.class文件&#xff0c;不能编译。 JVM (Java Virtual Machine)----Java虚拟…

十四、Vue 混入(Mixins)详解

文章目录 简介一、基本语法定义混入对象使用混入对象二、混入的数据合并数据合并规则深度合并(对象类型数据)三、混入的生命周期钩子生命周期钩子的合并规则利用生命周期钩子合并的优势四、混入的方法合并方法合并规则调用被覆盖的方法(高级用法)五、混入的应用场景多个组件…

简洁安装配置在Windows环境下使用vscode开发pytorch

简洁安装配置在Windows环境下使用vscode开发pytorch 使用anaconda安装pytorch&#xff0c;通过vscode集成环境开发pytorch 下载 anaconda 下载网址&#xff0c;选择对应系统的版本 https://repo.anaconda.com/archive/ windows可以选择Anaconda3-2024.10-1-Windows-x86_64.e…

【Linux】IP地址、主机名、网络传输、进程管理、主机状态

一、IP地址 1.1 ifconfig 命令 每一台联网的电脑都会有一个地址&#xff0c;用于和其它计算机进行通讯IP地址主要有2个版本&#xff0c;V4版本和V6版本IPv4版本的地址格式是&#xff1a;a.b.c.d&#xff0c;其中abcd表示0~255的数字&#xff0c;如192.168.88.101就是一个标准…