【LeetCode】从前序与中序遍历序列构造二叉树(不断优化)

#LeetCode每日一题【二叉树专题】

  • 从前序与中序遍历序列构造二叉树
    https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
  • 分析
    前序:根——左——右
    中序:左——根——右
    前序的第一个元素肯定是根节点,在中序中,根节点左边的元素是左子树的所有元素,根节点右边的元素是右子树的所有元素,在前序序列中找到左子树、右子树的前序序列,在递归的处理左右子树即可
  • 实现
	public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length == 0) {
            return null;
        }
        int rootValue = preorder[0], len = preorder.length;
        if (len == 1) {
            return new TreeNode(rootValue);
        }
        // 找到左子树、右子树的中序
        int location = 0;
        for (int i = 0; i < len; i++) {
            if (inorder[i] == rootValue) {
                location = i;
                break;
            }
        }
        int[] leftInorder = new int[location];
        int[] rightInorder = new int[len - location - 1];
        System.arraycopy(inorder, 0, leftInorder, 0, location);
        System.arraycopy(inorder, location + 1, rightInorder, 0, rightInorder.length);

        // 找到左子树的先序、右子树的先序
        int[] leftPreorder = new int[location];
        int[] rightPreorder = new int[rightInorder.length];
        System.arraycopy(preorder, 1, leftPreorder, 0, leftPreorder.length);
        System.arraycopy(preorder, 1 + leftPreorder.length, rightPreorder, 0, rightPreorder.length);

        TreeNode left = buildTree(leftPreorder, leftInorder);
        TreeNode right = buildTree(rightPreorder, rightInorder);
        return new TreeNode(rootValue, left, right);
    }

使用了大量的数组copy
LeetCode耗时:6ms
在这里插入图片描述

  • 优化
    涉及从原数组中copy一个新数组进行递归处理的,完全可以使用数组的下标来代替,开始下标、结束下标表示一个数组的范围。
public TreeNode buildTreeOptimize(int[] preorder, int[] inorder) {
        if (preorder == null || preorder.length == 0) {
            return null;
        }
        return buildTree(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
    }

    private TreeNode buildTree(int[] preorder, int[] inorder, int preorderStart, int preorderEnd,
                               int inorderStart, int inorderEnd) {
        // 递归结束条件
        if (preorderStart > preorderEnd) {
            return null;
        }
        // 先序的第一个元素是根节点
        int rootValue = preorder[preorderStart];
        // 根节点在中序中的位置
        int rootLocation = getIndexFromArray(rootValue, inorder, inorderStart, inorderEnd);
        // 左子树节点数、先序中序数组的长度
        int leftLength = rootLocation - inorderStart;
        // 找到左右子树先序中序开始、结束的下标
        TreeNode left = buildTree(preorder, inorder, preorderStart + 1, preorderStart + leftLength,
                inorderStart, rootLocation - 1);
        TreeNode right = buildTree(preorder, inorder, preorderStart + leftLength + 1, preorderEnd,
                rootLocation + 1, inorderEnd);
        return new TreeNode(rootValue, left, right);
    }

    private int getIndexFromArray(int rootValue, int[] inorder, int inorderStart, int inorderEnd) {
        for (int i = inorderStart; i <= inorderEnd; i++) {
            if (inorder[i] == rootValue) {
                return i;
            }
        }
        return -1;
    }

LeetCode耗时:3ms
在这里插入图片描述

  • 在优化
    看了题解,将中序遍历的结果先用一个map存储起来,便于后面每次可以根据值快速检索到下标,以空间换时间
Map<Integer, Integer> map = new HashMap<>();

    // 思路是正确的,找左子树的先序、中序;右子树的先序、中序,在递归地处理
    // 原方法是每次递归处理时都从原数组中复制出一个新的数组,存在大量的拷贝耗时
    // 基于这种情况,可以使用下标移动的方式,代替从原数组中进行拷贝,进行优化一下
    // 需要知道本轮递归先序开始的下标、结束的下标;中序开始的下标、结束的下标;
    public TreeNode buildTreeOptimize(int[] preorder, int[] inorder) {
        if (preorder == null || preorder.length == 0) {
            return null;
        }
        // 每次都需要从中序序列中找到根节点的位置,需要循环遍历一篇,可以考虑使用空间换时间,使用map提前存储好下标对应的值
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }
        return buildTree(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
    }

    private TreeNode buildTree(int[] preorder, int[] inorder, int preorderStart, int preorderEnd,
                               int inorderStart, int inorderEnd) {
        // 递归结束条件
        if (preorderStart > preorderEnd) {
            return null;
        }
        // 先序的第一个元素是根节点
        int rootValue = preorder[preorderStart];
        // 根节点在中序中的位置
//        int rootLocation = getIndexFromArray(rootValue, inorder, inorderStart, inorderEnd);
        Integer rootLocation = map.get(rootValue);
        // 左子树节点数、先序中序数组的长度
        int leftLength = rootLocation - inorderStart;
        // 找到左右子树先序中序开始、结束的下标
        TreeNode left = buildTree(preorder, inorder, preorderStart + 1, preorderStart + leftLength,
                inorderStart, rootLocation - 1);
        TreeNode right = buildTree(preorder, inorder, preorderStart + leftLength + 1, preorderEnd,
                rootLocation + 1, inorderEnd);
        return new TreeNode(rootValue, left, right);
    }

LeetCode耗时:1ms
在这里插入图片描述

  • 总结
  1. 熟悉前序中序后序的特性,从中找到构造树的规律
  2. 熟悉树的递归的应用
  3. 涉及从原数组中copy一个新数组进行递归处理的,完全可以使用数组的下标来代替,开始下标、结束下标表示一个数组的范围
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值