二叉树重建(衔接上一篇二叉树基本讲解)

【题目】输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树,假设输入的前序遍历和中序遍历的结果中都不含有重复的数字,例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},重建二叉树并输出头结点。


【分析】对根节点和左子树右子树分别分析

【根节点】前序遍历结果和中序遍历结果可以唯一确定一棵二叉树,前序遍历的过程就是从根结点开始,先访问根结点,再遍历左子树,最后右子树,从{1,2,4,7,3,5,6,8}可以看出1为根节点,中序遍历的过程是先遍历左子树,访问根节点,最后遍历右子树,从{4,7,2,1,5,3,8,6}就知道{4,7,2},{5,3,8,6}分别为根节点1的左子树和右子树,分别分析左子树和右子树。
【左子树】此时变成前序遍历序列{2,4,7},中序遍历序列{4,7,2},则对这个分支来说,2就是根节点1的左孩子,是4,7的父结点,也就是左子树分支的根结点,从{4,7,2}可以看出,{4,7}是结点2的左子树,结点2没有右子树,所以去除根节点2,考虑{4,7},很显然由前序序列{4,7},得到4作为根节点,但是从中序遍历序列看{4,7},7在4的右侧,说明4没有左子树,7是其右子树,只有7一个,所以,结点7为4的右孩子。整个以根节点1的左子树分析完毕。
【右子树】前序序列{3,5,6,8},中序序列{5,3,8,6},根节点为3,所以{5},{8,6}分别为其左右子树,左子树只有{5}所以结点5为3的左孩子,右子树前序为{6,8}中序为{8,6},根节点为6,剩下的8就是它的左孩子。整个以根节点1的右子树分析完毕。


【流程图】

将分析过程用下图来直观分析,始终变动的是root,相对各结点的子树作为根节点的变化情况,看图会更清晰,有助于对程序的理解。子树排列为左子树在左边,右子树在右边。
这里写图片描述


【二叉树重建图】

最终得到的二叉树如图所示:
这里写图片描述


【测试代码】

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

typedef struct BiTreeNode
{
    int data;
    struct BiTreeNode *left, *right;
}Node_t;


Node_t *construct(int *startPreorder, int *endPreorder, int *startInorder, int *endInorder)
{

    //前序遍历确定根节点
    int rootValue= startPreorder[0];
    Node_t *root =(Node_t *)malloc(sizeof(Node_t));
    root->data = rootValue;
    root ->left = root ->right = NULL;

    if(startPreorder == endPreorder)
    {
        if( startPreorder == endInorder && *startPreorder == *startInorder )
            return root;
    }
    //中序遍历确定左子树,右子树
    int *rootInorder = startInorder;

    while(rootInorder <= endInorder && *rootInorder != rootValue)
        ++rootInorder;
    if(rootInorder == endInorder && *rootInorder != rootValue)
    {
        printf("Invalid input");
        return  NULL;
    }
    int left_length = rootInorder - startInorder;
    int *left_Preorder_End = startPreorder + left_length;
    if(left_length>0)
    {
        //rebuild left tree
        root ->left = construct(startPreorder+1, left_Preorder_End, startInorder, rootInorder-1);
    }
    if(left_length < endPreorder - startPreorder)
    {
        //rebuild right tree
        root ->right =  construct(left_Preorder_End+1 , endPreorder, rootInorder+1, endInorder);
    }
    return root;
}
Node_t *create(int *preorder, int *inorder, int length)
{
    if( preorder == NULL || inorder == NULL || length <= 0)
        return NULL;

    return  construct(preorder, preorder + length -1, inorder, inorder + length -1);

}
//后序遍历
void post_order_visit(Node_t **T)
{
    if(*T == NULL)
        return ;
    else
    {
        post_order_visit(&(*T)->left);
        post_order_visit(&(*T)->right);
        printf("%d ",(*T)->data);
    }

}
int main()
{
    int pre_visit[] = {1, 2, 4, 7, 3, 5, 6, 8};
    int in_visit[] = {4, 7 ,2, 1, 5, 3, 8, 6};
    int *preorder = pre_visit;
    int *inorder = in_visit;

    Node_t *root =create(preorder, inorder, 8);
    post_order_visit(&root);

    return 0;
}

【输出结果】

这里写图片描述
程序参考剑指offer面试6里所给程序,实现的思路符合上述分析过程,所以有助于理解,从单步调试过程可以发现所建二叉树正确无误,在测试过程中分别测试以下几个方面,才能决定程序的可靠性全面性,
1.普通例子,如测试用例中给出的序列;
2.特殊例子,只有一个根节点,或所有节点都只有左节点或右结点,或者没有结点;
3.输入的前序和中序不匹配;分析一下前序和中序不匹配可能存在的问题,二者有一个正确,一个错误,可能是排序序列错误,输入长度错误,比如说中序错误中间某两个元素换位了,在上述步骤运行下会产生异常,从而抛出invalid input,由于c语言没有异常处理,只能返回异常代码,但是c++有恰当的机制来解决,这一点可以查看c++文档。


【二叉树重建延伸】

如果已知后序和中序序列,重建二叉树,中序序列{4,7,2,1,5,3,8,6},后序序列{7,4,2,5,8,6,3,1}


【分析+流程图】

由后序序列可以始终确定最后一位一定是二叉树的根root,由中序序列可以确定左子树成分和右子树成分,据此,利用流程图进行分析,
这里写图片描述
分析过程类似于前序序列和中序序列重建二叉树过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值