17.力扣-树-重构二叉树

力扣-树-重构二叉树

1.从前序与中序遍历序列构造二叉树(LeetCode 105)

  • 题目概述:给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点。(假设并无重复元素)
  • 题目案例:
    在这里插入图片描述
  • 解题思路:根据前序遍历找到根节点,然后将中序遍历划分为左右子树,然后左右子树进行同样的操作,即递归。这里有一个小细节就是用一个hashmap存放中序遍历的结点序号,方便查找(注意是以无重复元素为前提),这里直接放官方代码
  • 注意:划分左右子树时不要搞混与root的关系
class Solution {
    private Map<Integer,Integer> indexMap;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = preorder.length;
        // 构造哈希映射,帮助我们快速定位根节点
        indexMap = new HashMap<Integer, Integer>();
        for (int i = 0; i < n; i++) {
            indexMap.put(inorder[i], i);
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
    public TreeNode myBuildTree(int[] preorder,int[] inorder,int preorder_left,int preorder_right,int inorder_left,int inorder_right){
        if (preorder_left > preorder_right) {
            return null;
        }
        // 前序遍历中的第一个节点就是根节点
        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = indexMap.get(preorder[preorder_root]);
        // 先把根节点建立出来
        TreeNode root = new TreeNode(preorder[preorder_root]);
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root - inorder_left;
        // 递归地构造左子树,并连接到根节点
        // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
        // 递归地构造右子树,并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
        return root;
    }
}
  • java升级代码(与106解法同,解释可根据下面的106)
public class Solution {
    int[] preorder;
    int[] inorder;
    int pre_index;
    Map<Integer, Integer> idx_map = new HashMap<Integer, Integer>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder=preorder;
        this.inorder = inorder;
        pre_index=0;
        int idx=0;
        for(Integer val:inorder){
            idx_map.put(val,idx++);
        }
        return myBuild(0,inorder.length-1);
    }
    public TreeNode myBuild(int in_left,int in_right){
        if(in_left>in_right) return null;
        int root_val = preorder[pre_index];
        TreeNode root = new TreeNode(root_val);
        int index = idx_map.get(root_val);

        pre_index++;
        root.left = myBuild(in_left, index - 1);
        root.right = myBuild(index + 1, in_right);

        return root;
    }
}

2.从中序与后序遍历序列构造二叉树(LeetCode 106)

  • 题目概述:根据一棵树的中序遍历与后序遍历构造二叉树。(假设并无重复元素)

  • 题目案例:
    在这里插入图片描述

  • 解题思路:这题与105基本类似,通过后序遍历找到根节点的位置上,在根据中序遍历划分左右子树

  • 注意:这里最开始我用了和第一种一样的方法,显示超时(还不知道是什么原因,可能使个人原因),所以放了官方代码,与105相比做了一些升级。
    106的解法并未把后序遍历的数组及其左右边界编号作为参数传入,而是将数组作为全局变量,仅将每次分隔的index作为全局变量运用在方法中,这里很巧妙的把index放在全局变量中,这样递归时index的值就会改变,这里还要注意一定是先右后左,这样index- -才会正确,因为后序遍历的顺序是左右后,这样会简单很多。

class Solution {
    int post_idx;
    int[] postorder;
    int[] inorder;
    Map<Integer, Integer> idx_map = new HashMap<Integer, Integer>();

    public TreeNode helper(int in_left, int in_right) {
        // 如果这里没有节点构造二叉树了,就结束
        if (in_left > in_right) {
            return null;
        }

        // 选择 post_idx 位置的元素作为当前子树根节点
        int root_val = postorder[post_idx];
        TreeNode root = new TreeNode(root_val);

        // 根据 root 所在位置分成左右两棵子树
        int index = idx_map.get(root_val);

        // 下标减一
        post_idx--;
        // 构造右子树
        root.right = helper(index + 1, in_right);
        // 构造左子树
        root.left = helper(in_left, index - 1);
        return root;
    }

    public TreeNode buildTree(int[] inorder, int[] postorder) {
        this.postorder = postorder;
        this.inorder = inorder;
        // 从后序遍历的最后一个元素开始
        post_idx = postorder.length - 1;

        // 建立(元素,下标)键值对的哈希表
        int idx = 0;
        for (Integer val : inorder) {
            idx_map.put(val, idx++);
        }

        return helper(0, inorder.length - 1);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值