剑指Offer算法题之已知两种遍历方式重建二叉树--面试题6:重建二叉树

  • 直接上题目
    《剑指Offer算法题之已知两种遍历方式重建二叉树--面试题6:重建二叉树》

  • 本题所构建的二叉树如下图
    《剑指Offer算法题之已知两种遍历方式重建二叉树--面试题6:重建二叉树》

-本文已知二叉树的前序遍历和中序遍历,在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值再序列的中间,所以 左子树的结点的值位于根结点的值得左边,而右子树的结点的值位于根结点的值得右边。因此可知,我们需要扫描一遍中序遍历,找到根结点。

如下图可知,前序遍历序列的第一个数字1就是根结点,进过扫描中序遍历后找到根结点1所在位置。因此前面3个数字就是左子树结点值,位于1后面的数字都是右子树结点的值。

由于在中序遍历序列中,有3个数字是左子树结点的值,因此左子树总共有3个左子树结点。同样,在前序遍历的根结点后,也应该有3个结点从属于左子树,后面所有的结点从属于右子树。这样就分别找到了左右子树对应的子序列。

《剑指Offer算法题之已知两种遍历方式重建二叉树--面试题6:重建二叉树》

既然我们已经找到了中序遍历和前序遍历的左右子树,我们可以用相同的方法分别取构建左右子树。简单说就是用递归的方法去完整整个二叉树的重建。

  • 代码编写思路与注意点:
    1、根据题干可得,本题是通过前序遍历和中序遍历重建二叉树的。因此怎么由前序遍历和中序遍历获得后序遍历是关键。–根结点,是本题的突破口。在中序遍历序列中找到根结点是算法的第一步。
    2、因为题干的要求是输出根结点,无疑需要为根结点申请空间
    3、找到根结点的中序遍历,很清晰得出左子树和右子树的长度。通过这个长度也得出左子树和右子树的前序遍历。为了避免错误,可以列出每个子树头尾结点的位置。
    4、注意一个地方:尾结点=头结点+长度-1;,可以举例证实。
    5、使用递归继续求得左子树和右子树的根结点,并连接成二叉树。注意:需要判断左子树和右子树是否存在。
    6、注意特殊情况:1)当二叉树就只有一个结点时,直接输出根即可 2)当输入两个遍历出现问题时,直接报错:throw exception(“Invalid input”);
    7、非常重要,因为输入案例是:长度,前序和中序,而算法的形参是指针,因此需要重写一个模块,把输入数据转化为实参传入重建二叉树模块。
    8、根据重建的二叉树,按照每种遍历规则,输出序列。

  • 完整代码如下

#include "stdafx.h"
#include <iostream>

using namespace std;
//构建二叉树的结点
struct BinaryTreeNode
{
    int value;
    BinaryTreeNode* pLeft;
    BinaryTreeNode* pRight; 
};

BinaryTreeNode* Construct(int* preorder,int* inorder,int length);
//本题二叉树的核心算法

BinaryTreeNode* ConstructCore(int* startPreorder,int* endPreorder,int* startInorder,int* endInorder)
{
    //前序遍历的第一个结点就是根结点
    int rootValue =startPreorder[0];
    BinaryTreeNode* root=new BinaryTreeNode();
    //赋值,注意给左右孩子指针初始化
    root->value=rootValue;
    root->pLeft = root->pRight = NULL;//左子树和右子树需要初始化
    //先处理特殊情况,也就是树只剩一个结点的情况
    if(startPreorder==endPreorder)
    {
        if (startInorder==endInorder)
            return root;
        else
            throw exception("Invalid input");
    }
    //在中序遍历序列中找到根结点的值
    int* rootInorder= startInorder;
    while(*rootInorder!=rootValue&&rootInorder<=endInorder)
    {
        rootInorder++;
    }

    //找到根结点位置后,分别做出左右子树,及长度
    int leftLength=rootInorder-startInorder;
    //int rightLength=endInorder-rootInorder;
    //左子树的中序遍历
    int* leftstartInorder=startInorder;
    int* leftendInorder=rootInorder-1;
    //右子树的中序遍历
    int* rightstartInorder=rootInorder+1;
    int* rightendInorder=endInorder;
    //左子树的前序遍历
    int* leftstartPreorder=startPreorder+1;
    int* leftendPreorder=startPreorder+leftLength;//注意
    //右子树的前序遍历
    int* rightstartPreorder=leftendPreorder+1;
    int* rightendPreorder=endPreorder;
    //构建左子树--左子树长度>0
    if(leftLength>0)
    {
        root->pLeft=ConstructCore(leftstartPreorder,leftendPreorder,leftstartInorder,leftendInorder);

    }
    //构建右子树--
    if((endInorder-rootInorder)>0)
    {
        root->pRight=ConstructCore(rightstartPreorder,rightendPreorder,rightstartInorder,rightendInorder);
    }
    return root;
}

//由输入案例转化到核心算法的模块
BinaryTreeNode* Construct(int* preorder,int* inorder,int length)
{
    if(preorder==NULL||inorder==NULL||length<=0)
        return NULL;
    else
        return ConstructCore(preorder,preorder+length-1,inorder,inorder+length-1);
}
//二叉树的前序遍历
void PreorderTraverse(BinaryTreeNode* pNode)
{
    if(pNode==NULL)
        return ;

    cout<<pNode->value<<" ";
    PreorderTraverse(pNode->pLeft);
    PreorderTraverse(pNode->pRight);


}
//中序遍历
void InorderTraverse(BinaryTreeNode* pNode)
{
    if(pNode==NULL)
        return ;


    PreorderTraverse(pNode->pLeft);
    cout<<pNode->value<<" ";
    PreorderTraverse(pNode->pRight);


}
//后序遍历
void LastorderTraverse(BinaryTreeNode* pNode)
{
    if(pNode==NULL)
        return ;


    PreorderTraverse(pNode->pLeft);
    PreorderTraverse(pNode->pRight);
    cout<<pNode->value<<" ";

}


//调用测试模块
void Test(int* preorder,int* inorder,int length)
{
    BinaryTreeNode* root=Construct(preorder,inorder,length);

   printf("The preorder sequence is: \n");
   PreorderTraverse(root);
   cout<<endl;

   printf("The inorder sequence is:\n ");
   InorderTraverse(root);
   cout<<endl;

   printf("The Lastorder sequence is:\n ");
   LastorderTraverse(root);
   cout<<endl;
}

//输入测试案例
void Test1()
{
    const int length =8;
    int preorder[length]={1,2,4,7,3,5,6,8};
    int inorder[length]={4,7,2,1,5,3,8,6};

    Test(preorder,inorder,length);
}
// 普通二叉树
// 1
// / \
// 2 3 
// / / \
// 4 5 6
// \ /
// 7 8
int main()
{
    Test1();

    system("pause");
    return 0;
}
  • 测试结果如下:
    《剑指Offer算法题之已知两种遍历方式重建二叉树--面试题6:重建二叉树》
    原文作者:超人今天不会飞
    原文地址: https://blog.csdn.net/CSDN_564174144/article/details/76473305
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞