每日一练:LeeCode-106、从中序与后序遍历序列构造⼆叉树、LeeCode-106、从前序与中序遍历序列构造二叉树【二叉树+DFS+分治】

本文是力扣LeeCode-106、从中序与后序遍历序列构造二叉树 LeeCode-105、从前序与中序遍历序列构造二叉树 学习与理解过程,本文仅做学习之用,对本题感兴趣的小伙伴可以出门左拐LeeCode。

106、从中序与后序遍历序列构造⼆叉树

给定两个整数数组 inorderpostorder ,其中inorder是二叉树的中序遍历postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

示例 1:
每日一练:LeeCode-106、从中序与后序遍历序列构造⼆叉树、LeeCode-106、从前序与中序遍历序列构造二叉树【二叉树+DFS+分治】_第1张图片

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:
输入:inorder = [-1], postorder = [-1]
输出:[-1]

提示:

  • 1 <= inorder.length <= 3000
  • postorder.length == inorder.length
  • -3000 <= inorder[i], postorder[i] <= 3000
  • inorder 和 postorder 都由 不同 的值组成
  • postorder 中每一个值都在 inorder 中
  • inorder 保证是树的中序遍历
  • postorder 保证是树的后序遍历

思路

1. 判断是否为分治问题
原问题定义为从 postorder 和 inorder 构建二叉树,是一个典型的分治问题

  • 问题可以分解:从分治的角度切入我们可以将原问题划分为两个子问题构建左子树、构建右子树
  • 加上一步操作初始化根节点。而对于每棵子树(子问题),我们仍然可以复用以上划分方法,将其划分为更小的子树(子问题),直至达到最小子问题(空子树)时终止
  • 子问题是独立的左子树和右子树是相互独立的,它们之间没有交集。在构建左子树时,我们只需关注中序遍历和前序遍历中与左子树对应的部分。右子树同理
  • 子问题的解可以合并:一旦得到了左子树和右子树(子问题的解),我们就可以将它们链接到根节点上得到原问题的解

2. 如何划分子树
根据以上分析,这道题可以使用分治来求解,但如何通过后序遍历 postorder 和中序遍历 inorder 来划分左子树和右子树呢
根据定义,postorderinorder 都可以划分为三个部分。
后序遍历:[ 左子树 | 右子树 | 根节点] ,例如上图的树对应 [ 9 | 15 7 20 | 3 ] 。
中序遍历:[ 左子树 | 根节点 | 右子树 ] ,例如上图的树对应 [ 9 | 3 | 15 20 7 ] 。
所以根据上述定义,我们可以通过从后往前遍历后序遍历的数组拿到遍历的值作为根节点去切割中序遍历数组
比如:拿到3,可以将中序数组切割为 [ 9 | 3 | 15 20 7 ]

3. 基于变量描述子树区间
根据以上划分方法,我们已经得到根节点、左子树、右子树在 postorder和 inorder 中的索引区间。而为了描述这些索引区间,我们需要借助几个指针变量

  • 当前树的根节点在 postorder中的索引记为 postRootIndex
  • 当前树的根节点在 inorder 中的索引记为m
  • 当前树在 inorder 中的索引区间记为 [indexLeft, indexRight] ,这个区间会随着切割inorder后变化

代码实现

class Solution {
    private int postRootIndex = 0;  //当前树的根节点在 postorder中的索引
    private HashMap<Integer, Integer> inorderMap = new HashMap<>();
    private int[] gInorder;
    private int[] gPostorder;
    public TreeNode buildTree(int[] inorder, int[] postorder) {

        gInorder = inorder;
        gPostorder = postorder;
        int length = gPostorder.length;
        postRootIndex = length-1;
        // 初始化哈希表,存储 inorder 元素到索引的映射,避免重复遍历inorder
        for(int i=0;i<inorder.length;i++){
            inorderMap.put(inorder[i],i);
        }
        return dfs(0,length-1);
    }
    TreeNode dfs(int indexLeft,int indexRight){
        // 子树区间为空时终止
        if(indexLeft>indexRight)return null;

        int rootValue = gPostorder[postRootIndex];
        postRootIndex--;
        // 初始化根节点
        TreeNode root = new TreeNode(rootValue);
        // 查询 m ,从而划分左右子树,当前树的根节点在 inorder 中的索引
        int m = inorderMap.get(rootValue);
		//先构建左子树,还是右子树,需要根据提供的后序或者前序数组中节点的值的排列顺序决定
		//比如:此题是后序遍历,从后往前遍历postOrder,并生成节点,所以是: 中、右、左
        // 子问题:构建右子树
        root.right = dfs(m+1,indexRight);
        // 子问题:构建左子树
        root.left = dfs(indexLeft,m-1);
        return root;
    }
}

注: 先构建左子树,还是右子树,需要根据提供的后序或者前序数组中节点的值的排列顺序决定 比如:此题是后序遍历,从后往前遍历postOrder,并生成节点,所以是: 中、右、左

  • 时间复杂度为 ()
  • 空间复杂度为 ()

105、从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder inorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点

示例 1:每日一练:LeeCode-106、从中序与后序遍历序列构造⼆叉树、LeeCode-106、从前序与中序遍历序列构造二叉树【二叉树+DFS+分治】_第2张图片

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]
输出: [-1]

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorder 和 inorder 均 无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列

思路

此题和106的解题思路是一致的,但是需要注意
先构建左子树,还是右子树,需要根据提供的后序或者前序数组中节点的值的排列顺序决定 比如:此题是前序遍历,从前往后遍历preOrder,并生成节点,所以是: 中、左、右

代码实现

class Solution {
    private int preRootIndex = 0;   //当前树的根节点在 preorder中的索引
    private HashMap<Integer, Integer> inorderMap = new HashMap<>();
    private int[] gInorder;
    private int[] gPreorder;
    public TreeNode buildTree(int[] preorder, int[] inorder) {

        gInorder = inorder;
        gPreorder = preorder;
        int length = gPreorder.length;
        // 初始化哈希表,存储 inorder 元素到索引的映射,避免重复遍历inorder
        for(int i=0;i<inorder.length;i++){
            inorderMap.put(inorder[i],i);
        }
        return dfs(0,length-1);
    }
    TreeNode dfs(int indexLeft,int indexRight){
        // 子树区间为空时终止
        if(indexLeft>indexRight)return null;

        int rootValue = gPreorder[preRootIndex];
        preRootIndex++;
        // 初始化根节点
        TreeNode root = new TreeNode(rootValue);
        // 查询 m ,从而划分左右子树,当前树的根节点在 inorder 中的索引
        int m = inorderMap.get(rootValue);
        // 子问题:构建左子树
        root.left = dfs(indexLeft,m-1);
        // 子问题:构建右子树
        root.right = dfs(m+1,indexRight);
        return root;
    }
}
  • 时间复杂度为 ()
  • 空间复杂度为 ()

最重要的一句话:做二叉树的题目,首先需要确认的是遍历顺序
大佬们有更好的方法,请不吝赐教,谢谢

你可能感兴趣的:(#,每日一道LeeCode算法题,算法,数据结构,leetcode)