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开始遍历前序遍历数组,根据前序遍历的特点,当前节点要么是栈顶结点的左孩子,要么是栈顶结点或栈顶某个祖先节点的右孩子,我们可以结合中序遍历来判断当前节点是属于哪种情况,根据中序遍历的特点,若栈顶结点出现在中序遍历,那么意味着栈顶结点的左子树已经构建完成,我们弹出栈中所有左子树已经构建完成的节点,当前节点就是最后弹出的那个节点的右孩子。
相关推荐
极梦网络无忧1 小时前
password_hash
算法·哈希算法
计算机安禾2 小时前
【c++面向对象编程】第25篇:仿函数(函数对象):重载operator()
开发语言·c++·算法
周末也要写八哥2 小时前
在C++中使用预定义宏
开发语言·c++·算法
学会870上岸华师3 小时前
C 语言程序设计——第一章课后编程题
c语言·开发语言·学习·算法
小马过河R3 小时前
RAG检索优化策略:系统性四层框架解析
人工智能·python·算法·ai·llm·rag·问答
AI技术控3 小时前
论文解读:AE-TCN-SA——基于自编码器、TCN 与自注意力机制的锂电池内短路诊断方法
人工智能·python·深度学习·算法·机器学习·自然语言处理
ʚ希希ɞ ྀ3 小时前
动态规划基础知识---爬楼梯
算法·动态规划
计算机安禾3 小时前
【c++面向对象编程】第26篇:对象的内存模型:成员变量与成员函数的存储分离
开发语言·c++·算法
贩卖黄昏的熊4 小时前
陕西省ICPC省赛总结
算法