大一下数据结构编程实验——树形结构及其应用

以前,我总以为,树象征着生命,象征的生机;
后来,我遇到了那个它,我才知道,树会让你死亡,还是死无全尸的那种。。。

你以为的树:
在这里插入图片描述

实际中的树:
在这里插入图片描述
好家伙,先序后序中序,后序先序中序,中序后序先序,绕死一个算一个!!!

话不多说,题来(如果我被绑架了我就眨眨眼

题目1:
按先序序列建立二叉树,并输出该二叉树的中序遍历和后序遍历
输入:
先序序列:ABDH##I##E##CF#J##G##(#表示空)
输出:
中序序列:HDIBEAFJCG
后序序列: HIDEBJFGCA
注意:当只输入一个#时,需返回空二叉树
图解:
在这里插入图片描述
题目2:
给定一棵二叉树的后序和中序遍历序列,构造该二叉树,并输出该二叉树的前序遍历
输入:
后序序列: 3424321
中序序列: 3241423
输出:
前序序列: 1234243
注意:当输入的后序和中序遍历序列不能够构造出二叉树时,应输出:Unable to build a binary tree,这里的不能构造二叉树包括序列长度不一致、序列所含字符不一致以及不能够构造一棵二叉树等。
图解:
在这里插入图片描述
题目3:
给定一棵二叉树,返回该二叉树的叶子节点数和宽度
输入:
二叉树根节点:T
输出:
二叉树的叶子节点数:5
二叉树的宽度:4
图解:
在这里插入图片描述
题目4:
给定两棵二叉树,判断两棵二叉树是否等价。等价输出1,否则输出0
输入:
二叉树根节点:T
输出:
图1:1
图2:0
图解:
在这里插入图片描述
在这里插入图片描述

想必很多小伙伴们看完题后嗖嗖嗖地就把代码敲出来了吧(是我不配),就让我独自承受脱发的痛苦吧。

在这里插入图片描述

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

#define N 21  //后序序列和中序序列字符的最大个数为N-1

int FLAG; //标识能否由后序序列和中序序列建立二叉树,能为1,否为0

typedef struct Node
{
    char data;
    struct Node *lchild, *rchild;
}TreeNode, *BiTree;  //定义树节点的结构体

//函数功能:先序建立二叉树,并返回指向该二叉树的指针
BiTree CreateBiTree(void)
{
    BiTree bt = NULL;
    char data;
    if((data = getchar()) != '\n')
    {
        if(data == '#')
        {
            bt = NULL;  //字符#表示节点为空
        }
        else
        {
            bt = (BiTree)malloc(sizeof(TreeNode));
            if(!bt)
            {
                printf("OVERFLOW\n!");
                exit(0);
            }
            bt->data = data;  //树根
            bt->lchild = CreateBiTree();  //先序建立左子树
            bt->rchild = CreateBiTree();  //先序建立右子树
        }
    }
    return bt;
}

//函数功能:先序遍历二叉树
void PreOrderTraverse(BiTree T)
{
    if(T)
    {
        printf("%c", T->data);  //访问节点
        PreOrderTraverse(T->lchild);  //先序遍历左子树
        PreOrderTraverse(T->rchild);  //先序遍历右子树
    }
    return;
}

//函数功能:中序遍历二叉树
void InOrderTraverse(BiTree T)
{
    if(T)
    {
        InOrderTraverse(T->lchild);  //中序遍历左子树
        printf("%c", T->data);  //访问节点
        InOrderTraverse(T->rchild);  //中序遍历右子树
    }
    return;
}

//函数功能:后序遍历二叉树
void PostOrderTraverse(BiTree T)
{
    if(T)
    {
        PostOrderTraverse(T->lchild);  //后序遍历左子树
        PostOrderTraverse(T->rchild);  //后序遍历右子树
        printf("%c", T->data);  //访问节点
    }
    return;
}

//判断能否根据后序序列和中序序列构造二叉树,此函数的判定内容为后序序列和中序序列所含的字符是否相同
//说明:如果后序序列和中序序列能构造二叉树,那么其递归的左子树和右子树所含字符始终相同,当有任意一对左右子树含有不同字符时,输入的序列不能构成二叉树
void Judge(char post[], char in[], int num)
{
    for(int i = 0; i < num; i++)
    {
        if(!strchr(in, post[i]))  //后序序列含有中序序列所不含有的字符,不能构成二叉树
        {
            FLAG = 0;
            return;
        }
    }
    for(int i = 0; i < num; i++)  //中序序列含有后序序列所不含有的字符,不能构成二叉树
    {
        if(!strchr(post, in[i]))
        {
            FLAG = 0;
            return;
        }
    }
}

//函数功能:根据后序序列与中序序列构造二叉树,并返回指向该二叉树的指针
//参数说明:post[]为后序序列,in[]为中序序列,num为字符个数
BiTree PostInCreate(char post[], char in[], int num)
{
    BiTree bt = NULL;
    int i, num_left = 0, num_right = 0;
    char post_left[N], in_left[N], post_right[N], in_right[N];
    Judge(post, in, num);  //判断后序序列和前序序列能否构成二叉树
    if(num > 0 && FLAG)  //序列不为空且能够构成二叉树
    {
        bt = (BiTree)malloc(sizeof(TreeNode));
        bt->data = post[num-1];  //树根为后序序列的最后一个字符
        for(i = 0; i < num; i++)
        {
            if(in[i] == bt->data)  //求出树根在中序序列中的位置
                break;
        }
        num_left = i;  //左子树的节点个数
        num_right = num-i-1;  //右子树的节点个数
        for(i = 0; i < num_left; i++)
        {
            post_left[i] = post[i];  //左子树的后序序列
            in_left[i] = in[i];     //左子树的中序序列
        }
        for(i = 0; i < num_right; i++)
        {
            post_right[i] = post[num-num_right-1+i];  //右子树的后序序列
            in_right[i] = in[num-num_right+i];  //右子树的中序序列
        }
        bt->lchild = PostInCreate(post_left, in_left, num_left);  //根据后序序列与中序序列构造左子树
        bt->rchild = PostInCreate(post_right, in_right, num_right);  //根据后序序列与中序序列构造右子树
    }
    return bt;
}

//函数功能:计算并返回叶子节点的数量
int CountLeafSum(BiTree T)
{
    if(!T)  //空树
        return 0;
    if(T->lchild == NULL && T->rchild == NULL)  //只含树根节点
        return 1;
    return (CountLeafSum(T->lchild) + CountLeafSum(T->rchild));  //叶子节点的数量等于左子树叶子节点的数量+右子树叶子结点的数量
}

//函数功能:计算并返回二叉树的高度
int CountHeight(BiTree T)
{
    int ldepth, rdepth;
    if(!T)  //空树
        return 0;
    if(T->lchild == NULL && T->rchild == NULL)  //只含树根节点
        return 1;
    ldepth = CountHeight(T->lchild);  //左子树的高度
    rdepth = CountHeight(T->rchild);  //右子树的高度
    return ((ldepth > rdepth ? ldepth : rdepth) + 1);  //二叉树的高度是其左子树和右子树中较高的一个子树的高度加1
}

//函数功能:求出一个二叉树每一层的节点数量
//参数说明:数组count[]记录每一层的节点数
void GetWidth(BiTree T, int count[], char LEVEL[])
{
    struct Queue{
        BiTree Node;
        int level;
    }Q[N];
    int front = 0, rear = 0;
    if(!T)  //空树,直接返回
        return;
    else
    {
        Q[front].Node = T;  //队头为树根
        Q[front].level = 1;  //树根的层级为1
        while(front <= rear)
        {
            if(Q[front].Node->lchild)  //队头树根的左子树存在
            {
                Q[++rear].Node = Q[front].Node->lchild;  //队尾插入左子树的树根
                Q[rear].level = Q[front].level + 1;  //左子树树根的层级为当前树根的层级加1
            }
            if(Q[front].Node->rchild)  //队头树根的右子树存在
            {
                Q[++rear].Node = Q[front].Node->rchild;  //队尾插入右子树的树根
                Q[rear].level = Q[front].level + 1;  //右子树树根的层级为当前树根的层级加1
            }
            front++;  //队头指针下移
        }
        for(int i = 0; i <= rear; i++)
        {
            LEVEL[i+1] = Q[i].Node->data;  //层序遍历
            LEVEL[0]++;  //记录节点个数
            count[Q[i].level-1]++;  //遍历整个队列,统计各层级的节点数
        }
        return;
    }
}

//函数功能:判断两树是否等价,是返回1,否返回0
//参数说明:T1:第一棵二叉树,T2:第二棵二叉树
int IsEquivalent(BiTree T1, BiTree T2)
{
    int x = 0;
    if(!T1 && !T2)  //两棵树均为空树
    {
        x = 1;
    }
    else if((!T1 && T2) || (T1 && !T2))  //一棵为空树,另一棵不为空树
    {
        x = 0;
    }
    else  //两棵树均不为空树
    {
        if(T1->data == T2->data)  //节点信息相同
        {
            if(IsEquivalent(T1->lchild, T2->lchild))  //左子树等价
            {
                if(IsEquivalent(T1->rchild, T2->rchild))  //右子树等价
                {
                    x = 1;
                }
            }
        }
    }
    return x;
}

//任务1:实现二叉树的先序、中序、后序遍历
void Task1(BiTree T)
{
    printf("\nStart task (1) Create Tree in PreOrder\n");
    if(T)
    {
        printf("PreOrderTraverse:  ");
        PreOrderTraverse(T);
        printf("\n");
        printf("InOrderTraverse:  ");
        InOrderTraverse(T);
        printf("\n");
        printf("PostOrderTraverse:  ");
        PostOrderTraverse(T);
        printf("\n");
    }
    else  //空树
    {
        printf("The BiTree is NULL!\n");
    }
}

//任务2:根据后序序列和中序序列构造二叉树,当这两个序列不能构造二叉树时,输出"Can not build a BiTree!"
BiTree Task2(BiTree *T)
{
    printf("\nStart task (2) Input the postOrder and inOrder Sequence ,Then build the tree\n");
    char post[N] = {0};  //后序序列
    char in[N] = {0}; //中序序列
    int postlen, inlen;
    while(getchar() != '\n')continue;
    printf("Please input the postorder sequence(less than %d nodes):  ", N);
    scanf("%s",post);
    while(getchar() != '\n')continue;
    printf("Please input the inorder sequence(less than %d nodes):  ", N);
    scanf("%s",in);
    postlen = strlen(post);
    inlen = strlen(in);
    if(postlen != inlen)  //序列长度不同,不能构造二叉树
    {
        FLAG = 0;
    }
    else
    {
        *T = PostInCreate(post, in, postlen);  //递归构造二叉树
        if(FLAG)  //能够构造二叉树
        {
            printf("PreOrderTraverse:  ");
            PreOrderTraverse(*T);  //先序遍历二叉树
            printf("\n");
        }
    }
    if(!FLAG)  //不能构造二叉树
    {
        *T = NULL;  //空树
        printf("Can not build a BiTree!\n");
    }
    return *T;
}

//任务3:计算并打印二叉树的叶子节点个数、高度、宽度
void Task3(BiTree T)
{
    printf("\nStart task (3) ------------------------------\n");
    int maxwidth = 0;     //树的宽度,初始化为0
    int height;
    int i;
    int count[N] = {0};
    char LEVEL[N] = {0};
    height = CountHeight(T);  //树的高度
    printf("The number of leaf nodes of the tree is:  ");
    printf("%d\n",CountLeafSum(T));  //打印叶子节点的数量
    printf("The height of the tree is:  ");
    printf("%d\n", height);  //打印树的高度
    GetWidth(T, count, LEVEL);
    for(i = 0; i < height; i++)
    {

        if(count[i] > maxwidth)
        {
            maxwidth = count[i];
        }
    }
    printf("LevelOrderTraverse:  ");
    if(LEVEL[0] == 0)
    {
        printf("The BiTree is NULL!");
    }
    for(i = 1; i <= LEVEL[0]; i++)
    {
        printf("%c", LEVEL[i]);
    }
    for(i = 0; i < height; i++)
    {
        printf("\nThe No.%2d level has %d nodes.", i+1, count[i]);
    }
    printf("\nThe width of the tree is:  ");
    printf("%d\n", maxwidth);  //打印树的宽度
}

//任务4:判断两个二叉树是否等价
void Task4(BiTree T1,BiTree T2)
{
    printf("\nStart task (4) Are two Bitrees equivalent?");
    printf("(Equal: 1    Unequal: 0)\n");
    printf("Their relationship is %d.\n\n",IsEquivalent(T1, T2));
}

int main()
{
    BiTree Bt1, Bt2;
    char order;
    printf("1.Create BiTree_one with PRE    2.Create BiTree_two with POST and IN\n");
    printf("3.Count BiTree_one    4.Count BiTree_two    5.Judge whether they are equal\n");
    printf("Input your order(q to quit):  ");
    scanf("%c", &order);
    do{
        switch (order)
        {
            case'1':printf("Create BiTree_one in PreOrder(less than %d nodes):  ", N);
                    while(getchar() != '\n')continue;
                    Bt1=CreateBiTree();
                    Task1(Bt1);
                    break;
            case'2':FLAG = 1;
                    Task2(&Bt2);
                    break;
            case'3':Task3(Bt1);
                    break;
            case'4':Task3(Bt2);
                    break;
            case'5':Task4(Bt1, Bt2);
                    break;
            default:exit(0);
        }
        while(getchar() != '\n')continue;
        printf("\nInput your order(q to quit):  ");
    }while(scanf(" %c", &order));
    return 0;
}

在这里插入图片描述
虽然说这里有4个题目,但还是要把它们封装成一个程序,并且可以通过循环来让程序能够反复实现我们所需的功能(要是提交4个程序给老师,我怕是不想活了

敲黑板!!!
可能很多人都觉得我发这些实验有用吗,都没什么干货,题也不分析,知识点也不总结,就给出题干代码来骗赞。。。
在这里插入图片描述

你这么说我就不乐意了(还是蛮赞同的*),发题主要是为了看到这篇文章的小伙伴们能够动脑动手敲一敲,在不懂的地方可以参考一下我的代码(帮我找找bug呀 ),里面也有部分解析(我承认确实是部分 ),知识点啥的呢以后会分点细讲的(手动单曲循环《后来》 ),所以还请大家多多包涵和支持,共同进步啊!!!

凉梦空间

欢迎你进入我的个人博客网站参观交流:https://www.liangmeng.xyz
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

凉丶梦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值