当前位置: 首页 > news >正文

【LeetCode】从前序与中序遍历序列构造二叉树 [M](二叉树重构)

105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

一、题目

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

示例 1:

输入: 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 保证 为二叉树的中序遍历序列

二、代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    // 优化算法,将原本每一次调用都要遍历一遍找到find的操作,变成总总共只需要遍历一次,存入数组中,以后再去取find直接从数组中就可以获取到,不用便利了,这样整个复杂度变成了O(N)
    public static TreeNode buildTree(int[] pre, int[] in) {
        if (pre == null || in == null || pre.length != in.length) {
            return null;
        }

        // 创建一个Map用来存放中序遍历中每一个值所在的位置
        HashMap<Integer, Integer> valueIndexMap = new HashMap<>();
        for (int i = 0; i < in.length; i++) {
            valueIndexMap.put(in[i], i);
        }

        // 调用递归
        return g(pre, 0, pre.length - 1, in, 0, in.length - 1, valueIndexMap);
    }

    // 有一棵树,先序结果是pre[L1...R1],中序结果是in[L2...R2]
    // 请建出整棵树返回头节点

    // 这个思路就是根据中序根节点左边是其左子树,右边使其右子树,然后前序遍历每一段的最开始节点一定是根节点。通过这个特点来构造出整个二叉树
    // 关键是找到递归出口
    public static TreeNode g(int[] pre, int L1, int R1, int[] in, int L2, int R2,
                             HashMap<Integer, Integer> valueIndexMap) {

        // 这种情况当前节点已经没有左右子节点了,这个时候如果还按照递归的写法L1 + 1,就会导致得到的数已经超过了有边界,这种情况说明遍历到了最底层的空节点,直接返回空
        if (L1 > R1) {
            return null;
        }

        // 递归出口,当先序遍历递归到了只剩下一个节点,说明这个肯定是一个根节点,返回。这里是所有递归返回的开始,从这里开始,这一个分支的递归就不会再向下一层继续了,而是还是向上层返回。因为这个返回位置在继续向下递归调用的方法代码的前面。
        TreeNode head = new TreeNode(pre[L1]);
        if (L1 == R1) {
            return head;
        }

        // 找到当前节点在中序遍历中的位置,用来分割左右子树
        int find = valueIndexMap.get(pre[L1]);
        // 先通过中序遍历获取一下下一层递归所涉及到的数组范围,左子树就是L2~find - 1
        // 这样我们就知道总共涉及到find - 1 - L2个元素,这样先序遍历设计的范围就是,L1+1 ~ L1 + 1 + find - 1 - L2,这样就把这一段子树的左右子节点都涵盖进去了
        head.left = g(pre, L1 + 1, L1 + find - L2, in, L2, find - 1, valueIndexMap);
        // 右子树递归同理
        head.right = g(pre, L1 + find - L2 + 1, R1, in, find + 1, R2, valueIndexMap);

        // 走到这一步直接返回当前根节点,将每一层当前的节点都向上一层返回,这样才这逐层构建出二叉树结构。这一步就是连接每一层递归的接口,用来将下层结果数据传递给上一层,逐层传递,返回到最上层时最终得到结果
        return head;
    }
}

三、解题思路 

通过这道题,我们就知道了递归出口就是当先序遍历的数组只剩下一个的时候,说明已经签不遍历完了,就将这个节点直接返回即可。向下递归的位置就是那两行调用g()方法的方法,这里解释不断地划分左右子树,缩小数据范围。递归节后就是最后的return head,通过这个来讲每一层的递归调用返回给上一层。

每一轮返回的head都是当前层子树的根节点。也就是当前层先序遍历的第一个元素。然后再根据中序遍历找到头节点位置,再将中序遍历头节点左右两边的范围分别向下递归,这样就将剩下的子树再一次划分成左右子树向下递归了。

相关文章:

  • C++GUI之wxWidgets(6)-一步步做zip精灵(1)
  • [vue element-ui]JAVA POST请求
  • 【C语言 全局 整形变量 布尔变量 数组变量 指针变量 结构体位域变量 枚举变量被其他.C文件相互访问】
  • MyBatis的增删改查操作
  • TypeScript基础类型
  • 汇聚数据库创新力量,加速企业数字化转型
  • python实战案例——采集二手车数据并分析其价值
  • Java EE 程序修改题【太原理工大学】
  • Promise:工作流程、常见API、使用方法、手撕Promise、async/await
  • Vue3 中选项式下的侦听器
  • 有向图的拓扑序列
  • 防御 CSS 黑客——介绍“安全的 CSS hacks”
  • 通信原理与MATLAB(九):DPSK的调制解调
  • Docker安装配置运行Redis
  • 2022 年上海市大学生程序设计竞赛 M. My University Is Better Than Yours
  • __proto__ 和 prototype的关系
  • 【EOS】Cleos基础
  • 03Go 类型总结
  • ES6简单总结(搭配简单的讲解和小案例)
  • extjs4学习之配置
  • php的插入排序,通过双层for循环
  • Promise面试题,控制异步流程
  • Python_OOP
  • React系列之 Redux 架构模式
  • Vue 重置组件到初始状态
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 技术:超级实用的电脑小技巧
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 免费小说阅读小程序
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 设计模式 开闭原则
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 数据结构java版之冒泡排序及优化
  • 小程序测试方案初探
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • (2)STM32单片机上位机
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (算法设计与分析)第一章算法概述-习题
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (一)插入排序
  • (状压dp)uva 10817 Headmaster's Headache
  • .net Application的目录
  • .net 简单实现MD5
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • .Net组件程序设计之线程、并发管理(一)
  • ::前边啥也没有
  • @JoinTable会自动删除关联表的数据
  • [ SNOI 2013 ] Quare
  • [20150904]exp slow.txt
  • [Algorithm][动态规划][简单多状态DP问题][按摩师][打家劫舍Ⅱ][删除并获得点数][粉刷房子]详细讲解