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

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

题目链接:https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/description/?envType=study-plan-v2\&envId=top-100-liked

我的解答:

复制代码

分析:知道原理,但是实现上难以理清思路。

看了官方题解后的解答:

复制代码
//方法一:递归
//时间复杂度:O(n)
//空间复杂度:O(n)
Map<Integer,Integer> map = new HashMap<>();//key:值    value:值在inorder中对应的下标
public TreeNode buildTree(int[] preorder, int[] inorder) {
    int n = preorder.length;
    //构造哈希映射,方便快速定位根节点在inorder中的位置
    for(int i = 0; i < n; i++){
        map.put(inorder[i], i);
    }
    return myBuildTree(preorder, inorder, 0, n-1, 0, n-1);
}

public TreeNode myBuildTree(int[] preorder, int[] inorder, int pLeft, int pRight, int iLeft, int iRight){
    if(pLeft > pRight){
        return null;
    }
    //前序遍历中的第一个节点就是根节点
    TreeNode root = new TreeNode(preorder[pLeft]);
    //在中序遍历中定位根节点
    int rootInInorder = map.get(preorder[pLeft]);
    //得到左子树中的节点数目
    int leftCount = rootInInorder - iLeft;
    //递归地构造左子树,并连接到根节点
    root.left = myBuildTree(preorder, inorder, pLeft+1, pLeft+leftCount, iLeft, rootInInorder-1);
    //递归地构造右子树,并连接到根节点
    root.right = myBuildTree(preorder, inorder, pLeft+leftCount+1, pRight, rootInInorder+1, iRight);
    return root;
}

//方法二:迭代
//时间复杂度:O(n)
//空间复杂度:O(n)
public TreeNode buildTree(int[] preorder, int[] inorder) {
    Deque<TreeNode> stack = new LinkedList<>();//维护左子树还未构建完成的节点
    TreeNode root = new TreeNode(preorder[0]);
    stack.push(root);
    int inorderIndex = 0;
    for(int i = 1; i < preorder.length; i++){
        TreeNode top = stack.peek();//栈顶的节点
        int topVal = top.val;//栈顶结点的值
        TreeNode cur = new TreeNode(preorder[i]);
        if(topVal != inorder[inorderIndex]){//栈顶结点的左子树还未遍历完,当前节点应为栈顶结点的左孩子
            top.left = cur;
            stack.push(cur);
        }
        else{//栈顶结点的左子树已经遍历完,当前节点应为栈顶结点或其某个祖先节点的右孩子
            while(!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]){
                top = stack.pop();
                inorderIndex++;
            }
            top.right = cur;
            stack.push(cur);
        }
    }
    return root;
}

分析:

​ 1、方法一的解题思路:利用哈希表预处理中序遍历数组,方便快速定位计算左、右子树节点总数。每次维护当前节点作为根节点的子树在前序遍历和中序遍历中的范围,在范围内,第一个节点就是当前子树的根节点,创建出根节点,继续递归构造当前根节点的左子树和右子树即可。

​ 2、方法二的解题思路:利用栈维护还未构建右子树的节点,遍历前序遍历数组,创建出当前节点,当前节点只有两种情况:

​ 情况一:当前栈顶节点的值与中序遍历的值不相等,那么当前节点就是栈顶结点的左孩子;

​ 情况二:当前栈顶节点的值与中序遍历的值相等,那么当前节点是栈顶结点或栈顶某个祖先节点的右孩子。

​ 对于情况一,我们直接将当前节点作为栈顶结点的左孩子并将当前节点入栈即可;

​ 对于情况二,栈顶节点的值出现在中序遍历,意味着栈顶结点的左子树已经构造完成,所以当前节点要么是栈顶结点的右孩子,要么是栈顶某个祖先节点的右孩子,我们只需要不断从栈中弹出所有左子树已经构建完成的节点,最后弹出的那个节点就是当前节点的父节点。我们只需要将当前节点作为最后弹出的那个节点的右孩子并将当前节点入栈即可。

总结

  • 本题主要有递归和迭代两种方法解题。
  • 对于递归方法:只需要维护每个节点作为根节点时,整棵树的节点分别在前序遍历和中序遍历的范围;再利用栈预处理中序遍历,方便快速定位当前节点的位置,并迅速计算出当前节点的左子树节点总数和右子树节点总数。
  • 对于迭代方法:用栈维护左子树未构建完成的节点,先构建出二叉树的根节点并入栈,然后从下标1开始遍历前序遍历数组,根据前序遍历的特点,当前节点要么是栈顶结点的左孩子,要么是栈顶结点或栈顶某个祖先节点的右孩子,我们可以结合中序遍历来判断当前节点是属于哪种情况,根据中序遍历的特点,若栈顶结点出现在中序遍历,那么意味着栈顶结点的左子树已经构建完成,我们弹出栈中所有左子树已经构建完成的节点,当前节点就是最后弹出的那个节点的右孩子。
相关推荐
林爷万福4 分钟前
光谱数据预处理:基线校正、平滑去噪实战
人工智能·算法
8Qi818 分钟前
LeetCode 1049:最后一块石头的重量 II —— 题解 ✅
算法·leetcode·职场和发展·动态规划·01背包
wubba lubba dub dub75032 分钟前
第四十九周学习周报
人工智能·算法·机器学习
Java_2017_csdn1 小时前
ComplexKeysShardingAlgorithm 小结
java·大数据·算法
海梨花1 小时前
快手面试高频算法题
java·算法·面试
lqqjuly1 小时前
超分辨率算法深度解析(Super-Resolution Algorithms)
算法
嵌入式老牛3 小时前
液晶段码(米/日字格)识别—倾斜校正
opencv·算法·仿射变换
luj_17683 小时前
残熵算法:风险缓冲与效率优化的融合
c语言·开发语言·网络·经验分享·算法
oddsand13 小时前
pgvector 三大相似度算法
人工智能·算法·机器学习
运筹vivo@3 小时前
LeetCode 2574. 左右元素和的差值
算法·leetcode·职场和发展·每日一题