牛客网题目链接:重建二叉树

0 题目描述:

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

1、题目分析

题目给出前序遍历和中序遍历,让重建二叉树。由前序遍历特性知前序遍历的第一个节点,肯定是根节点。那么找到了根节点,接下来找到左子树与右子树即可。由中序遍历的特性知中序遍历中,根节点位于中间某一个位置,它的左边是左子树节点,右边是右子树的节点。从而我们就找到了左子树的所有节点与右子树的所有节点以及根节点。然后我们让根节点左指针指向左子树的根节点,根节点右子树指针指向右子树的根节点。那么接下来的问题就是要求左子树的根节点与右子树的根节点,然后再让左子树的根节点指向它的左子树与右子树,右子树的根节点指向它的左子树与右子树…很明显,这是一个递归过程。

下面我们看以下图示,来理解找根节点,左子树与右子树的过程。

假设某一棵树如下:

它的前序遍历位:[1,2,4,7,3,5,6,8] 后序遍历:[4,7,2,1,5,3,8,6]

那么第一次找根节点与左右子树,如下图:

此时,可以得出如下我们要求的二叉树是如下样式:

以上我们只知道左子树和右子树包含哪些节点,并不知道左子树和右子树长什么样。

接下来我们再递归的求解左子树序列与右子树序列。,找到左子树的根节点与右子树的根节点。

递归求解左子树:

此时,可以得出如下二叉树的形式:

同理,此时对于左子树的根节点2,再对其递归求解它的左子树与右子树的二叉树,其中右子树位空不用求解直接指向null,左子树还有节点,那么利用左子树的节点的前序序列与中序序列,再次递归求解即可。

最终得出如下图所示的左子树二叉树:

递归求解右子树:

左子树递归求解完后,就递归求解右子树:

此时可以得到如下二叉树的形式:

同理,测试再对于右子树的节点3,再对其进行递归求它的左子树与右子树,最终得到右子树的二叉树为:

所以,最终的二叉树就是如下图:

2、代码

2.1、java代码

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre.length-1==0 || in.length-1==0 || pre.length != in.length)return null;
        return reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
    }
    public TreeNode reConstructBinaryTree(int[] pre,int preStart,int preEnd,int[] in,int inStart,int inEnd){
        int rootVal=pre[preStart];
        TreeNode root=new TreeNode(rootVal);//根节点
        root.left=root.right=null;
        if(preStart==preEnd || inStart==inEnd)return root;//如果只有一个数了则只有一个节点
        int inIndex=inStart;//代表中序遍历中的根节点的位置
        while(in[inIndex]!=rootVal && inIndex<=inEnd)inIndex++;//找到这个inIndex的位置
        int lnum=inIndex-inStart;//左子树节点的个数
        int rnum=inEnd-inIndex;//右子树节点的个数
        if(lnum>0){
            root.left=reConstructBinaryTree(pre,preStart+1,preStart+lnum,in,inStart,inIndex-1);
        }
        if(rnum>0){
            root.right=reConstructBinaryTree(pre,preStart+lnum+1,preEnd,in,inIndex+1,inEnd);
        }
        return root;
    }
}

注释:代码中inIndex代表中序遍历中根节点的位置,lnum代表左子树节点的个数,rnum代表右子树节点的个数。

2.2 C++代码

class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        if(pre.size()==0 || vin.size()==0 || pre.size()!=vin.size())return nullptr;
        return reConstructBinaryTree(pre,0,pre.size()-1,vin,0,vin.size()-1);
    }
    TreeNode* reConstructBinaryTree(vector<int> pre, int preStart,int preEnd,
                                    vector<int> vin, int vinStart,int vinEnd){
        int rootVal=pre[preStart];
        TreeNode* root = new TreeNode(rootVal);//根节点
        root->left=nullptr;
        root->right=nullptr;
        if(preStart == preEnd || vinStart==vinEnd)return root;//如果只有一个数了则只有一个节点
        int vIndex = vinStart;//代表中序遍历中的根节点的位置
        while(rootVal != vin[vIndex] && vIndex <= vinEnd)vIndex++;//找到这个vIndex 的位置
        int lnum = vIndex-vinStart;//左子树节点的个数
        int rnum = vinEnd-vIndex;//右子树节点的个数
        int leftIndex = preStart+lnum;
        if(lnum>0){
            root->left = reConstructBinaryTree(pre,preStart+1,leftIndex,vin,vinStart,vIndex-1);
        }
        if(rnum>0){
            root->right = reConstructBinaryTree(pre,leftIndex+1,preEnd,vin,vIndex+1,vinEnd);
        }
        return root;
    }
};

注释:上面vIndex代表中序遍历中根节点的位置,lnum代表左子树的节点数目,rnum代表右子树的节点的数目。

3、总结

以上代码并不是最简洁的代码,但是目的是为了突出这种算法的实现过程。希望每个人都能很容易就理解这种递归的过程。

学习探讨加:
qq:1126137994
微信:liu1126137994