剑指 Offer 07. 重建二叉树

49 阅读2分钟

一. 题目

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

image.png
示例1Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

二: 思路

所有构建二叉树问题的思路都是分三步: 找到根节点, 找到左子树, 找到右子树

这道题给了前序遍历和中序遍历的结果,我们如何利用二者的特点构建二叉树呢?

前序遍历的顺序, 根结点 --> 左子树 --> 右子树
中序遍历的顺序, 左子树 --> 根节点 --> 右子树
从二者的特点中我们可以发现,从前序遍历中很容易找到根节点(序列的第一个元素一定是根节点),
当我们找到根节点后,我们就能从中序遍历中找到他的左子树和右子树

可能大家有个疑惑,我找到的左(右)子树是个范围啊,并不是一个具体的节点,我的指针如何指向具体的节点呢?
我们可以通过递归或者dfs算法缩小这个范围,最终就能确定到一个左节点或右节点
image.png

三: 代码实现

1. 递归
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return build(preorder,inorder,0,preorder.length - 1,0,inorder.length - 1);
    }
    
    TreeNode build(int[] preorder, int[] inorder,int preStart,int preEnd,int inoStart,int inoEnd){
        //base case
        if(preStart > preEnd || inoStart > inoEnd){
            return null;
        }
        //树的根节点
        int val = preorder[preStart];
        TreeNode root =  new TreeNode(val);
        //查找根节点在中序遍历的位置
        int index = 0; 
        for(int i = inoStart;i <= inoEnd;i++){
            if(inorder[i] == val){
                index = i;
                break;
            }
        }
        //计算左子树在数组中的长度
        int leftSize = index - inoStart;
        //递归构建。
        root.left = build(preorder,inorder,preStart + 1,preStart + leftSize,inoStart , index - 1);
        root.right = build(preorder,inorder,preStart + leftSize + 1,preEnd,index + 1 , inoEnd);
        return root;
    }
}
2.分治
class Solution {

    int[] preorder;
    HashMap<Integer, Integer> dic = new HashMap<>();
    
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder = preorder;
        for(int i = 0; i < inorder.length; i++)
            dic.put(inorder[i], i);
        return recur(0, 0, inorder.length - 1);
    }
    
    TreeNode recur(int root, int left, int right) {
        if(left > right){
            return null;                          // 递归终止
        }
        // 建立根节点
        TreeNode node = new TreeNode(preorder[root]);
        // 划分根节点、左子树、右子树
        int i = dic.get(preorder[root]);      
        // 开启左子树递归
        node.left = recur(root + 1, left, i - 1);       
        // 开启右子树递归
        node.right = recur(root + i - left + 1, i + 1, right); 
         // 回溯返回根节点
        return node;                                          
    }
}