力扣之重建二叉树练习

在计算机科学中,二叉树是一种基础且重要的数据结构,广泛应用于各种算法中。给定二叉树的遍历结果,重建原始二叉树是一类常见问题。本文将介绍如何根据二叉树的不同遍历序列(前序+中序、中序+后序、前序+后序)来重建二叉树。

基础知识

  • 前序遍历(Pre-order Traversal):访问顺序为 根节点 → 左子树 → 右子树。
  • 中序遍历(In-order Traversal):访问顺序为 左子树 → 根节点 → 右子树。
  • 后序遍历(Post-order Traversal):访问顺序为 左子树 → 右子树 → 根节点。

重建二叉树

最近连续三天出现了重建二叉树的练习,题目主要是围绕这几种遍历中选取两种结果(数组形式输入)然后得出一颗完整二叉树(层序遍历)输出。(对应链接分别附于标题后)

1. 前序遍历 + 中序遍历

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

原理:前序遍历的第一个元素是根节点,中序遍历中根节点将树分为左右子树。利用这些信息,我们可以定位根节点,并递归地构建左右子树。

构建二叉树的原理基于先序遍历和中序遍历的特性。在先序遍历中,遍历的顺序是根节点→左子树→右子树,而在中序遍历中,遍历的顺序是左子树→根节点→右子树。利用这两个特性,我们可以确定二叉树的根节点以及哪些节点属于左子树和右子树。

步骤解析

  1. 确定根节点

    • 先序遍历的第一个元素总是树的根节点,因为先序遍历总是先访问根节点。
  2. 分割中序遍历

    • 在中序遍历中找到根节点后,根节点左侧的所有元素都属于左子树,根节点右侧的所有元素都属于右子树。这是因为中序遍历的顺序是先左子树,然后是根节点,最后是右子树。
  3. 递归构建子树

    • 一旦我们知道了哪些节点属于左子树,我们就可以从先序遍历中找到左子树的根节点(即左子树的第一个元素),并递归地构建左子树。
    • 同理,我们也可以找到右子树的根节点,并递归地构建右子树。
  4. 重复上述过程

    • 对于每个子树,都重复上述过程,直到所有的节点都被包含在树中。

关键点

  • 先序遍历确定根节点:先序遍历始终是从根节点开始,所以它可以帮助我们快速定位到每个子树的根节点。
  • 中序遍历分割子树:中序遍历中,根节点的位置可以帮助我们明确哪些节点属于左子树,哪些属于右子树。
  • 递归构建:通过递归方法,我们可以从顶向下构建整个二叉树,每次递归都会创建一个新的树节点,并将其连接到其父节点。
  • 索引映射:为了提高查找效率,我们可以提前将中序遍历的值和它们的索引存储在一个映射中,这样就可以在 O(1) 的时间内找到任何值的索引。

代码实现

java 复制代码
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    private int preIndex = 0;
    private Map<Integer, Integer> inMap = new HashMap<>();

    public TreeNode buildTreeFromPreIn(int[] preorder, int[] inorder) {
        for (int i = 0; i < inorder.length; i++) inMap.put(inorder[i], i);
        return construct(preorder, 0, inorder.length - 1);
    }

    private TreeNode construct(int[] preorder, int inStart, int inEnd) {
        if (inStart > inEnd) return null;
        TreeNode root = new TreeNode(preorder[preIndex++]);
        int inIndex = inMap.get(root.val);
        root.left = construct(preorder, inStart, inIndex - 1);
        root.right = construct(preorder, inIndex + 1, inEnd);
        return root;
    }
}

2. 中序遍历 + 后序遍历

106. 从中序与后序遍历序列构造二叉树

原理:后序遍历的最后一个元素是根节点,中序遍历中根节点将树分为左右子树。通过定位根节点,我们可以递归地构建左右子树。

代码实现

java 复制代码
public class Solution {
    private int postIndex;
    private Map<Integer, Integer> inMap = new HashMap<>();

    public TreeNode buildTreeFromInPost(int[] inorder, int[] postorder) {
        postIndex = postorder.length - 1;
        for (int i = 0; i < inorder.length; i++) inMap.put(inorder[i], i);
        return construct(inorder, postorder, 0, inorder.length - 1);
    }

    private TreeNode construct(int[] inorder, int[] postorder, int inStart, int inEnd) {
        if (inStart > inEnd) return null;
        TreeNode root = new TreeNode(postorder[postIndex--]);
        int inIndex = inMap.get(root.val);
        //顺序交换,先递归右子树
        root.right = construct(inorder, postorder, inIndex + 1, inEnd);
        root.left = construct(inorder, postorder, inStart, inIndex - 1);
        return root;
    }
}

3. 前序遍历 + 后序遍历

889. 根据前序和后序遍历构造二叉树

原理:利用前序遍历确定根节点,后序遍历确定左右子树的范围。然而,仅凭这两种遍历结果,二叉树可能无法被唯一确定。当我们使用先序遍历和后序遍历数组来重建二叉树时,我们利用了这样一个事实:先序遍历的顺序是"根-左-右",而后序遍历的顺序是"左-右-根"。这意味着先序遍历的第一个元素是树的根节点,而后序遍历的最后一个元素也是同一个根节点。使用这些信息,我们可以按照以下步骤进行递归:

代码实现

java 复制代码
public class Solution {
    int preorderIndex =0;
    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {
        for (int i = 0; i < postorder.length; i++){
            map.put(postorder[i],i);
        }
        return arrayToTree(preorder,postorder,0,preorder.length-1);
    }
    public TreeNode arrayToTree(int[] preorder, int[] postorder,int left,int right) {
        if (left > right || preorderIndex >= preorder.length) {
            return null;
        }

        TreeNode root = new TreeNode(preorder[preorderIndex++]);
        if (left == right || preorderIndex >= preorder.length) {
            return root;
        }

        int index = map.get(preorder[preorderIndex]);
        
        root.left = arrayToTree(preorder, postorder, left, index);
        root.right = arrayToTree(preorder, postorder, index + 1, right - 1);
        return root;
    }
}

总结

重建二叉树是一个深入理解二叉树遍历和递归算法的好例子。每种遍历方法的组合提供了不同的信息,我们可以利用这些信息来还原原始的树结构。需要注意的是,不是所有遍历组合都能唯一确定一棵二叉树,特别是在没有附加条件(如二叉搜索树等)的情况下。正确地理解和应用这些原理对于解决相关的算法问题至关重要。

相关推荐
Wx-bishekaifayuan6 分钟前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
oliveira-time9 分钟前
golang学习2
算法
customer0810 分钟前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
全栈开发圈12 分钟前
新书速览|Java网络爬虫精解与实践
java·开发语言·爬虫
WaaTong15 分钟前
《重学Java设计模式》之 单例模式
java·单例模式·设计模式
面试鸭17 分钟前
离谱!买个人信息买到网安公司头上???
java·开发语言·职场和发展
沈询-阿里1 小时前
java-智能识别车牌号_基于spring ai和开源国产大模型_qwen vl
java·开发语言
AaVictory.1 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
南宫生1 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法