LeetCode算法小抄--二叉树的各种构造

news2024/11/26 6:19:20

LeetCode算法小抄--各种情况的构造二叉树

    • 构造二叉树
      • 构造最大二叉树
        • [654. 最大二叉树](https://leetcode.cn/problems/maximum-binary-tree/)
      • 从前序与中序遍历构造二叉树
        • [105. 从前序与中序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)[面试笔试必考题]
      • 从中序与后序遍历构造二叉树
        • [106. 从中序与后序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/)
      • 从前序和后序遍历构造二叉树
        • [889. 根据前序和后序遍历构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-postorder-traversal/)

⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计758字,阅读大概需要3分钟
🌈更多学习内容, 欢迎👏关注👀【文末】我的个人微信公众号:不懂开发的程序猿
个人网站:https://jerry-jy.co/

构造二叉树

构造最大二叉树

654. 最大二叉树

给定一个不重复的整数数组 nums最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边子数组前缀上 构建左子树。
  3. 递归地在最大值 右边子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树

/**
 * 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 {
    public TreeNode constructMaximumBinaryTree(int[] nums) {
        return build(nums, 0, nums.length - 1);
    }

    // 定义:将 nums[lo..hi] 构造成符合条件的树,返回根节点
    private TreeNode build(int[] nums, int lo, int hi){
        // base case
        if(lo > hi) return null;

        // 找到数组中的最大值和对应的索引
        int index = -1, maxVal = Integer.MIN_VALUE;
        for(int i = lo; i <= hi; i++){
            if(maxVal < nums[i]) {
                index = i;
                maxVal = nums[i];
            }
        }

        // 先构造出根节点
        TreeNode root = new TreeNode(maxVal);
        // 递归调用构造左右子树
        root.left = build(nums, lo, index - 1);
        root.right = build(nums, index + 1, hi);

        return root;
    }
}

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

105. 从前序与中序遍历序列构造二叉树[面试笔试必考题]

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

在这里插入图片描述

思路:

1、要想办法确定根节点的值,把根节点做出来,然后递归构造左右子树即可。找到根节点是很简单的,前序遍历的第一个值 preorder[0] 就是根节点的值。

2、关键在于如何通过根节点的值,将 preorderinorder 数组划分成两半,构造根节点的左右子树?

伪代码

/* 主函数 */
public TreeNode buildTree(int[] preorder, int[] inorder) {
    // 根据函数定义,用 preorder 和 inorder 构造二叉树
    return build(preorder, 0, preorder.length - 1,
                 inorder, 0, inorder.length - 1);
}

/* 
    build 函数的定义:
    若前序遍历数组为 preorder[preStart..preEnd],
    中序遍历数组为 inorder[inStart..inEnd],
    构造二叉树,返回该二叉树的根节点 
*/
TreeNode build(int[] preorder, int preStart, int preEnd, 
               int[] inorder, int inStart, int inEnd) {
    // root 节点对应的值就是前序遍历数组的第一个元素
    int rootVal = preorder[preStart];
    // rootVal 在中序遍历数组中的索引
    int index = 0;
    for (int i = inStart; i <= inEnd; i++) {
        if (inorder[i] == rootVal) {
            index = i;
            break;
        }
    }

    TreeNode root = new TreeNode(rootVal);
    // 递归构造左右子树
    root.left = build(preorder, ?, ?,
                      inorder, ?, ?); // 这几个问号处应该填什么

    root.right = build(preorder, ?, ?,
                       inorder, ?, ?); // 这几个问号处应该填什么
    return root;
}

代码中的 rootValindex 变量,就是下图这种情况

在这里插入图片描述

题目说二叉树节点的值不存在重复,所以可以使用一个 HashMap 存储元素到索引的映射,这样就可以直接通过 HashMap 查到 rootVal 对应的 index

对于左右子树对应的 inorder 数组的起始索引和终止索引比较容易确定

在这里插入图片描述

对于 preorder 数组呢?如何确定左右数组对应的起始索引和终止索引?

假设左子树的节点数为 leftSize,那么 preorder 数组上的索引情况是这样的:

在这里插入图片描述

索引情况:

int leftSize = index - inStart;

root.left = build(preorder, preStart + 1, preStart + leftSize,
                  inorder, inStart, index - 1);

root.right = build(preorder, preStart + leftSize + 1, preEnd,
                   inorder, index + 1, inEnd);

最终代码:

/**
 * 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 {
    // 存储 inorder 中值到索引的映射
    HashMap<Integer, Integer> valToIndex = new HashMap<>();

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        for(int i = 0; i < inorder.length; i++){
            valToIndex.put(inorder[i], i);
        }
        return build(preorder, 0, preorder.length - 1,
                     inorder, 0, inorder.length - 1);
    }
    private TreeNode build( int[] preorder, int preStart, int preEnd,
                            int[] inorder, int inStart, int inEnd){
        if(preStart > preEnd) return null;

        // root 节点对应的值就是前序遍历数组的第一个元素
        int rootVal  = preorder[preStart];
        // rootVal 在中序遍历数组中的索引
        int index = valToIndex.get(rootVal);

        int leftSize = index - inStart;

        // 先构造出当前根节点
        TreeNode root = new TreeNode(rootVal);
        // 递归构造左右子树
        root.left = build(preorder, preStart + 1, preStart + leftSize,
                            inorder, inStart, index - 1);
        root.right = build(preorder, preStart + leftSize + 1, preEnd,
                            inorder, index + 1, inEnd);
        return root;
    }
}

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

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

给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

在这里插入图片描述

/**
 * 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 {
    // 存储 inorder 中值到索引的映射
    HashMap<Integer, Integer> valToIndex = new HashMap<>();

    public TreeNode buildTree(int[] inorder, int[] postorder) {
        for(int i = 0; i < inorder.length; i++){
            valToIndex.put(inorder[i], i);
        }
        return build(inorder, 0, inorder.length - 1,
                     postorder, 0, postorder.length - 1);
    }

    private TreeNode build( int[] inorder, int inStart, int inEnd,
                            int[] postorder, int postStart, int postEnd){
        if(inStart > inEnd) return null;

        // root 节点对应的值就是前序遍历数组的第一个元素
        int rootVal  = postorder[postEnd];
        // rootVal 在中序遍历数组中的索引
        int index = valToIndex.get(rootVal);

        int leftSize = index - inStart;

        // 先构造出当前根节点
        TreeNode root = new TreeNode(rootVal);
        // 递归构造左右子树
        root.left = build(inorder, inStart, index - 1,
                            postorder, postStart, postStart + leftSize - 1);
        root.right = build(inorder, index + 1, inEnd,
                            postorder, postStart + leftSize, postEnd - 1);
        return root;
    }    
}

从前序和后序遍历构造二叉树

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

给定两个整数数组,preorderpostorder ,其中 preorder 是一个具有 无重复 值的二叉树的前序遍历,postorder 是同一棵树的后序遍历,重构并返回二叉树。

如果存在多个答案,您可以返回其中 任何 一个。

这道题和前两道题有一个本质的区别:

通过前序中序,或者后序中序遍历结果可以确定唯一一棵原始二叉树,但是通过前序后序遍历结果无法确定唯一的原始二叉树

思路:

用后序遍历和前序遍历结果还原二叉树,解法逻辑上和前两道题差别不大,也是通过控制左右子树的索引来构建:

1、首先把前序遍历结果的第一个元素或者后序遍历结果的最后一个元素确定为根节点的值

2、然后把前序遍历结果的第二个元素作为左子树的根节点的值

3、在后序遍历结果中寻找左子树根节点的值,从而确定了左子树的索引边界,进而确定右子树的索引边界,递归构造左右子树即可

在这里插入图片描述

/**
 * 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 {
    // 存储 inorder 中值到索引的映射
    HashMap<Integer, Integer> valToIndex = new HashMap<>();

    public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {
        for(int i = 0; i < postorder.length; i++){
            valToIndex.put(postorder[i], i);
        }
        return build(preorder, 0, preorder.length - 1,
                     postorder, 0, postorder.length - 1);
    }

    // 定义:根据 preorder[preStart..preEnd] 和 postorder[postStart..postEnd]
    // 构建二叉树,并返回根节点。
    private TreeNode build( int[] preorder, int preStart, int preEnd,
                            int[] postorder, int postStart, int postEnd){
        if(postStart > postEnd) return null;
        if(preStart == preEnd) return new TreeNode(preorder[preStart]);

        // root 节点对应的值就是前序遍历数组的第一个元素
        int rootVal  = preorder[preStart];
        // root.left 的值是前序遍历第二个元素
        // 通过前序和后序遍历构造二叉树的关键在于通过左子树的根节点
        // 确定 preorder 和 postorder 中左右子树的元素区间
        int leftRootVal = preorder[preStart + 1];
        // rootVal 在后序遍历数组中的索引
        int index = valToIndex.get(leftRootVal);

        int leftSize = index - postStart + 1;

        // 先构造出当前根节点
        TreeNode root = new TreeNode(rootVal);
        // 递归构造左右子树
        root.left = build(preorder, preStart + 1, preStart + leftSize,
                            postorder, postStart, index);
        root.right = build(preorder, preStart + leftSize + 1, preEnd,
                            postorder, index + 1, postEnd - 1);
        return root;
    }    
}

代码和前两道题非常类似,我们可以看着代码思考一下,为什么通过前序遍历和后序遍历结果还原的二叉树可能不唯一呢?

关键在这一句:

int leftRootVal = preorder[preStart + 1];

我们假设前序遍历的第二个元素是左子树的根节点,但实际上左子树有可能是空指针,那么这个元素就应该是右子树的根节点。由于这里无法确切进行判断,所以导致了最终答案的不唯一。

总结:

二叉树的构造问题一般都是使用「分解问题」的思路:构造整棵树 = 根节点 + 构造左子树 + 构造右子树。先找出根节点,然后根据根节点的值找到左右子树的元素,进而递归构建出左右子树。

–end–

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/424045.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity和UE有啥区别?哪个更适合游戏开发

游戏制作软件中最著名的两个游戏引擎是 Unity 和 Unreal Engine。从独立游戏到大型工作室&#xff0c;许多游戏开发商都在使用它们。如果你打算从事游戏行业工作&#xff0c;你肯定曾经问过自己“我的游戏应该使用 Unity 还是 Unreal Engine&#xff1f;” ” 让我们来了解和比…

ActiveMQ使用(四):在JavaScript中发送的MQTT消息在SpringBoot中变为字节数组

ActiveMQ使用(四):在JavaScript中发送的MQTT消息在SpringBoot中变为字节数组 1. 问题描述 JmsListener(destination "test_producer", containerFactory "topicListenerContainer")public void receiveTestProducer(String message) throws JMSExceptio…

AI绘画兴起,Stable Diffusion脱颖而出,来一探究竟

近几年&#xff0c;AI图像生成风靡全球&#xff0c;它能够根据文字描述生成精美图像&#xff0c;这极大地改变了人们的图像创作方式。众多专业人士说该技术正在引领着新一轮深度学习创意工具浪潮&#xff0c;并有望彻底改变视觉媒体的创作。 AI绘画兴起 Stable Diffusion脱颖…

[Django] 后台管理系统

浏览之前&#xff0c;请先阅读以下文章 1.Django项目创建 2.Django路由系统 在项目目录下的urls.py文件中&#xff0c;我们会看到这样一个url的配置 启动服务&#xff0c;在浏览器中输入网址http://127.0.0.1:8000/admin/&#xff0c;结果如下 Django提供了一个非常强大的管…

前端解析Excel中的数据进行操作

技术要点&#xff1a;Vue、Element、JSON 功能描述&#xff1a;读取Excel中的数据&#xff0c;利用JavaScript技术奖数据转成Json格式进行操作&#xff01; 功能描述&#xff1a;只能用前端操作数据&#xff0c;并未实现将数据传送至后端处理&#xff01; 注意注意注意 如果…

Dapper——分布式跟踪系统

分布式跟踪系统 背景 当代的互联网的服务&#xff0c;通常都是用复杂的、大规模分布式集群来实现的。互联网应用构建在不同的软件模块集上&#xff0c;这些软件模块&#xff0c;有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器&#xff0…

常用加密算法

目录 常见的加密算法可以分成三种&#xff1a; 对称加密算法 DES 3DES AES 非对称加密 RSA ECC Hash算法 MD5 SHA1 算法对比 算法选择 常见的加密算法可以分成三种&#xff1a; 对称加密算法&#xff1b;非对称加密算法&#xff1b;Hash算法&#xff1b;接下来我们…

论文 : Multi-Kernel Broad Learning systems Based on Random Features

Multi-Kernel Broad Learning systems Based on Random Features:A Novel Expansion for Nonlinear Feature Nodes 基于核方法的强大性能&#xff0c;本文提出了一种基于多核的BLS系统扩展方法。首先&#xff0c;将多核形式的非线性特征映射合并为广义学习系统的特征节点; 然后…

液晶显示器输入信号接口(一) —— VGA

VGA1. 简介2. VGA接口3. VGA线4. 技术原理4.1 信号传输4.2 模拟信号的扫描方式4.3 参数本文主要介绍VGA&#xff0c;其它输入输出信号接口介绍可从以下链接跳转&#xff1a; 液晶显示器输入信号接口(二) —— DVI 液晶显示器输入信号接口(三) —— HDMI 液晶显示器输入信号接口…

Mysql高级 学习笔记分享

索引&#xff1a;Index是帮助Mysql高效获取数据的数据结构 索引是一种数据结构 ---排好序的快速查找数据结构 、 某种满足特定查找算法的数据结构、以某种方式指向数据 两大功能&#xff1a;查找快、排好序 目的在于提高查询效率&#xff0c;类比字典 如果没有索引&a…

【Vue】学习笔记-列表渲染/数据监视

列表渲染/数据监视基本列表Key的作用与原理列表过滤列表排序Vue 数据监视原理基本列表 v-for指令 用于展示列表数据语法&#xff1a;v-for“(item,index) in xxx” :key“yyy”可遍历&#xff1a;数组&#xff0c;对象&#xff0c;字符串&#xff08;用的很少&#xff09;&am…

中国农业大学821数据结构经验贴

中国农业大学821经验贴目录个人情况说明初试复习经验数学二英语二政治数据结构复试复习经验目录 个人情况说明 初试成绩 复试成绩 复试成绩在平均水平&#xff0c;综合成绩第五 本科经历 河南双非GPA&#xff1a;3.79&#xff0c;专业排名第6竞赛经历&#xff1a;CCPC省银&…

Matlab进阶绘图第16期—三维填充折线图

三维填充折线图是在三维折线图的基础上&#xff0c;对其与XOY平面之间的部分进行颜色填充&#xff0c;从而能够更好地刻画细节变化。 由于Matlab中未收录三维填充折线图的绘制函数&#xff0c;因此需要大家自行设法解决。 本文使用自制的FilledPlot3小工具进行三维填充折线图…

计算机网络第一章(概述)【湖科大教书匠】

1. 各种网络 网络(Network)由若干**结点(Node)和连接这些结点的链路(Link)**组成多个网络还可以通过路由器互连起来&#xff0c;这样就构成了一个覆盖范围更大的网络&#xff0c;即互联网(互连网)。因此&#xff0c;互联网是"网络的网络(Network of Networks)"**因特…

扩散模型的Prompt指南:如何编写一个明确提示

Prompt&#xff08;提示&#xff09;是扩散模型生成图像的内容来源&#xff0c;构建好的提示是每一个Stable Diffusion用户需要解决的第一步。本文总结所有关于提示的内容&#xff0c;这样可以让你生成更准确&#xff0c;更好的图像 一个好的提示 首先我们看看什么是好的提示…

MCM箱模型建模方法及大气O3来源解析

详情点击链接&#xff1a;MCM箱模型建模方法及大气O3来源解析一、大气中O3形成、MCM和Atchem 2原理及Linux系统安装1.大气中O3形成的原理 2、MCM原理及基本流程3、Atchem 2 下载安装4、Linux系统安装5、Atchem 2 运行需要的其他工具A、Fortran&#xff1b;B、Python&#xff1…

小程序分包(普通分包、独立分包)

普通分包和独立分包的区别 当小程序从普通的分包页面启动的时候&#xff0c;首先需要下载主包独立分包是不依赖看于主包就可以运行&#xff0c;提升分包的执行速度&#xff0c;一个小程序可以有多个独立分包。 独立分包属于分包的一种。普通分包的所有限制都对独立分包有效。独…

基于LDA+SVM实现人脸识别模型

基于LDASVM实现人脸识别模型 描述 人脸识别&#xff08;图像识别&#xff09;是机器学习领域十经典的应用&#xff0c;在本质上&#xff0c;人脸识别属于监督学习中的分类问题。前面章节中我们已经学习了支持向量机&#xff08;SVM&#xff09;&#xff0c;该算法在图像分类领…

专为智能设备安全打造 | 基于ACM32 MCU的智能断路器方案

随着我国电网建设的快速发展&#xff0c;数字化变电站成为建设和研究的热点&#xff0c;数字化变电站的核心在于一次设备的智能化与二次设备的网络化&#xff0c;对于断路器这种极其重要的电力一次设备而言&#xff0c;其智能化的实现有十分重要的意义&#xff0c;断路器智能化…

耗时半月,终于把牛客网上的软件测试面试八股文整理成了PDF合集(测试基础+linux+MySQL+接口测试+自动化测试+测试框架+jmeter测试+测试开发)

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了&#xff0c;考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些程序员了。 这不&#xf…