算法题:剑指 Offer 07. 重建二叉树(题目+思路+代码+注释)时空时间优先选O(N) O(N) 2ms击败97%、76%用户,空间优先则选O(N*logN) O(1)

在这里插入图片描述

题目

剑指 Offer 07. 重建二叉树

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

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

示例 1:
在这里插入图片描述

Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
示例 2:

Input: preorder = [-1], inorder = [-1]
Output: [-1]

限制:

0 <= 节点个数 <= 5000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

题目中的null你可能不太理解,那是打印的算法上,与直接理解打印数不一样,他打印了非叶子节点的左右节点,空的就是null,这个不在乎这么多,我们要的是重建这颗二叉树并且返回根节点对象就可以了

第一步观察数据,根据二叉树遍历特性找规律,逆向解出来。
preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
首先第一点,在前序遍历的时候第一个数字一定是根节点,那么我们拿到了根节点,如何去获取左右的子节点呢?
根据中序遍历的特性,根节点在最中间,因此我们拿着从前序遍历里得到的3到中序遍历里找到3的下标,于是乎在中序遍历里3的左边的数字全部属于左子树,3右边的数字全部是右子树的。也就是左边的9是左子树,15 20 7是右子树,这样我们就知道了左右子树有多少个数字了,回到前序遍历中,前序遍历是: 根节点 左子树 右子树,而左子树的第一个就是根节点的左子节点,右子树的第一个就是右子树的第一个节点,因此我们就可以找出根节点的左右节点,按照这个方法递归下去,就能把每个节点的左右节点找出来,最后整颗二叉树就建好了。我们详细步骤来解释下:
在这里插入图片描述

代码

提交代码


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length == 0){
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);
        HashMap<Integer,Integer> map = new HashMap<>(preorder.length);
        for (int i = 0,len = preorder.length; i < len;i++){
            map.put(inorder[i],i);
        }
        rebuild(map,root,preorder,inorder,0,0,preorder.length-1);
        return root;
    }

    /**
     * 重建二叉树函数
     * @param map 中序遍历 k:数字 v:下标
     * @param root 当前子树的根节点对象
     * @param preorder 前序遍历数组
     * @param inorder 中序遍历数组
     * @param rootInPreOrderIndex  根节点在前序遍历数组中的下标
     * @param left 当前root点为根节点的子树在中序遍历数组中的起始下标
     * @param right 当前root点为根节点的子树在中序遍历数组中的终止下标
     */
    public void rebuild(HashMap<Integer,Integer> map,TreeNode root,int[] preorder, int[] inorder,int rootInPreOrderIndex,int left,int right){
        if (left < right){
            int rootNum = preorder[rootInPreOrderIndex];
            int rootInOrderIndex = map.get(rootNum);
            int leftTreeSize = rootInOrderIndex - left;
            int rightTreeSize = right - rootInOrderIndex;
            /*System.out.println("当前节点"+rootNum+" root在先序遍历上的下标:"+rootInPreOrderIndex+"左右子树在中序遍历上的 left:"+left+" right:"+right+" 左子树大小:"+leftTreeSize+" 右子树大小:"+rightTreeSize);
            System.out.println("整颗树");
            for (int i = left; i<= right;i++){
                System.out.print(preorder[i]+" ");
            }
            System.out.println();
            System.out.println("左子树");
            for (int i = 0; i< leftTreeSize;i++){
                System.out.print(preorder[left+i+1]+" ");
            }
            System.out.println();
            System.out.println("右子树");
            for (int i = 0; i< rightTreeSize;i++){
                System.out.print(preorder[left+leftTreeSize+i+1]+" ");
            }
            System.out.println();*/
            //左子树
            if (leftTreeSize > 0){
                TreeNode leftNode = new TreeNode(preorder[rootInPreOrderIndex+1]);
                root.left = leftNode;
                //以左节点为根继续递归建立子树
                rebuild(map,root.left,preorder,inorder,rootInPreOrderIndex+1,left,rootInOrderIndex-1);
            }
            //右子树
            if (rightTreeSize > 0){
                TreeNode rightNode = new TreeNode(preorder[rootInPreOrderIndex+leftTreeSize+1]);
                root.right = rightNode;
                rebuild(map,root.right,preorder,inorder,rootInPreOrderIndex+leftTreeSize+1,rootInOrderIndex+1,right);
            }
        }
    }
}

测试代码(有提示打印)

附带对二叉树前序遍历、中序遍历、后序遍历的打印函数以供测试用
对递归中执行的每一步打印输出关键数据,便于理解,解除注释即可

package leetcode.offer.q07;

import java.util.HashMap;

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length == 0){
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);
        HashMap<Integer,Integer> map = new HashMap<>(preorder.length);
        for (int i = 0,len = preorder.length; i < len;i++){
            map.put(inorder[i],i);
        }
        rebuild(map,root,preorder,inorder,0,0,preorder.length-1);
        return root;
    }

    /**
     * 重建二叉树函数
     * @param map 中序遍历 k:数字 v:下标
     * @param root 当前子树的根节点对象
     * @param preorder 前序遍历数组
     * @param inorder 中序遍历数组
     * @param rootInPreOrderIndex  根节点在前序遍历数组中的下标
     * @param left 当前root点为根节点的子树在中序遍历数组中的起始下标
     * @param right 当前root点为根节点的子树在中序遍历数组中的终止下标
     */
    public void rebuild(HashMap<Integer,Integer> map,TreeNode root,int[] preorder, int[] inorder,int rootInPreOrderIndex,int left,int right){
        if (left < right){
            int rootNum = preorder[rootInPreOrderIndex];
            int rootInOrderIndex = map.get(rootNum);
            int leftTreeSize = rootInOrderIndex - left;
            int rightTreeSize = right - rootInOrderIndex;
            /*System.out.println("当前节点"+rootNum+" root在先序遍历上的下标:"+rootInPreOrderIndex+"左右子树在中序遍历上的 left:"+left+" right:"+right+" 左子树大小:"+leftTreeSize+" 右子树大小:"+rightTreeSize);
            System.out.println("整颗树");
            for (int i = left; i<= right;i++){
                System.out.print(preorder[i]+" ");
            }
            System.out.println();
            System.out.println("左子树");
            for (int i = 0; i< leftTreeSize;i++){
                System.out.print(preorder[left+i+1]+" ");
            }
            System.out.println();
            System.out.println("右子树");
            for (int i = 0; i< rightTreeSize;i++){
                System.out.print(preorder[left+leftTreeSize+i+1]+" ");
            }
            System.out.println();*/
            //左子树
            if (leftTreeSize > 0){
                TreeNode leftNode = new TreeNode(preorder[rootInPreOrderIndex+1]);
                root.left = leftNode;
                //以左节点为根继续递归建立子树
                rebuild(map,root.left,preorder,inorder,rootInPreOrderIndex+1,left,rootInOrderIndex-1);
            }
            //右子树
            if (rightTreeSize > 0){
                TreeNode rightNode = new TreeNode(preorder[rootInPreOrderIndex+leftTreeSize+1]);
                root.right = rightNode;
                rebuild(map,root.right,preorder,inorder,rootInPreOrderIndex+leftTreeSize+1,rootInOrderIndex+1,right);
            }
        }
    }

    /**
     * 前序遍历
     * @param root
     */
    public void preOrderPrintTree(TreeNode root){
        if (root != null){
            System.out.print(root.val+" ");
            if (root.left != null){
                preOrderPrintTree(root.left);
            }
            if (root.right != null){
                preOrderPrintTree(root.right);
            }
        }
    }

    /**
     * 中序遍历
     * @param root
     */
    public void inOrderPrintTree(TreeNode root){
        if (root != null){
            if (root.left != null){
                inOrderPrintTree(root.left);
            }
            System.out.print(root.val+" ");
            if (root.right != null){
                inOrderPrintTree(root.right);
            }
        }
    }

    /**
     * 后序遍历
     * @param root
     */
    public void sufOrderPrintTree(TreeNode root){
        if (root != null){
            if (root.left != null){
                sufOrderPrintTree(root.left);
            }
            if (root.right != null){
                sufOrderPrintTree(root.right);
            }
            System.out.print(root.val+" ");
        }
    }
    public static void main(String[] args) {
        Solution solution = new Solution();
        //前序遍历、中序遍历、后序遍历示范
        /*TreeNode root = new TreeNode(3);
        root.left = new TreeNode(9);
        root.right = new TreeNode(20);
        TreeNode tmp = root.right;
        tmp.left = new TreeNode(15);
        tmp.right = new TreeNode(7);
        solution.preOrderPrintTree(root);
        System.out.println();
        solution.inOrderPrintTree(root);
        System.out.println();
        solution.sufOrderPrintTree(root);*/

        int[] preOrder = {1,2,3};
        int[] inOrder = {3,2,1};
        TreeNode root = new Solution().buildTree(preOrder,inOrder);
        solution.preOrderPrintTree(root);
    }
}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int x) {
        val = x;
    }
}

空间优先代码(去掉了哈希表,手动在子树内搜索下标)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length == 0){
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);
        rebuild(root,preorder,inorder,0,0,preorder.length-1);
        return root;
    }

    public void rebuild(TreeNode root,int[] preorder, int[] inorder,int rootIndex,int left,int right){
        if (left < right){
            int rootNum = root.val;
            int rootInOrderIndex = left;
            while (rootInOrderIndex <= right){
                if (inorder[rootInOrderIndex] == rootNum){
                    break;
                }else {
                    rootInOrderIndex++;
                }
            }
            int leftTreeSize = rootInOrderIndex - left;
            int rightTreeSize = right - rootInOrderIndex;
            /*System.out.println("当前节点"+rootNum+" root在先序遍历上的下标:"+rootInPreOrderIndex+"左右子树在中序遍历上的 left:"+left+" right:"+right+" 左子树大小:"+leftTreeSize+" 右子树大小:"+rightTreeSize);
            System.out.println("整颗树");
            for (int i = left; i<= right;i++){
                System.out.print(preorder[i]+" ");
            }
            System.out.println();
            System.out.println("左子树");
            for (int i = 0; i< leftTreeSize;i++){
                System.out.print(preorder[left+i+1]+" ");
            }
            System.out.println();
            System.out.println("右子树");
            for (int i = 0; i< rightTreeSize;i++){
                System.out.print(preorder[left+leftTreeSize+i+1]+" ");
            }
            System.out.println();*/
            //左子树
            if (leftTreeSize > 0){
                TreeNode leftNode = new TreeNode(preorder[rootIndex+1]);
                root.left = leftNode;
                //以左节点为根继续递归建立子树
                rebuild(root.left,preorder,inorder,rootIndex+1,left,rootInOrderIndex-1);
            }
            //右子树
            if (rightTreeSize > 0){
                TreeNode rightNode = new TreeNode(preorder[rootIndex+leftTreeSize+1]);
                root.right = rightNode;
                rebuild(root.right,preorder,inorder,rootIndex+leftTreeSize+1,rootInOrderIndex+1,right);
            }
        }
    }
}
posted @ 2021-08-26 14:36  HumorChen99  阅读(0)  评论(0编辑  收藏  举报  来源