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开始遍历前序遍历数组,根据前序遍历的特点,当前节点要么是栈顶结点的左孩子,要么是栈顶结点或栈顶某个祖先节点的右孩子,我们可以结合中序遍历来判断当前节点是属于哪种情况,根据中序遍历的特点,若栈顶结点出现在中序遍历,那么意味着栈顶结点的左子树已经构建完成,我们弹出栈中所有左子树已经构建完成的节点,当前节点就是最后弹出的那个节点的右孩子。
相关推荐
wabs66616 小时前
关于图论【卡码网117.软件构建的思考】
数据结构·算法·软件构建·图论·卡码网
mifengxing17 小时前
LeetCode 41.缺失的第一个正数|Hard题O(n)+O(1)最优解法深度解析
java·算法·leetcode·排序算法
Tongzhi202617 小时前
从部署到运维:通芝科技无感考勤一体机的全流程效率解析
运维·数据结构·科技·算法·贪心算法
Wang's Blog17 小时前
AI Agent白手起家30: 动态示例选择器之根据长度选择 Few Shot 示例
算法
oyguyteggytrrwwwrt19 小时前
自制交叉线路识别算法
算法
手写码匠19 小时前
华为云Flexus+DeepSeek征文|Dify 多智能体协同编排实战:R1 规划 + V3 执行,构建企业 Agent 团队
人工智能·深度学习·算法·aigc
晓天衡宇•评测社区20 小时前
FSR-Bench 前沿科学推理榜单发布:GPT-5.5 居首,“会推理”未必“能答对”
算法
程序喵大人21 小时前
【C++进阶】STL算法与函数对象 - 02 sort为什么需要随机访问迭代器
开发语言·c++·算法
hanlin0321 小时前
动态规划专练:力扣第121、122题
笔记·算法·leetcode
To_OC21 小时前
LC 74 搜索二维矩阵:换皮的二分查找,我居然一开始没看出来
javascript·算法·leetcode