Java LeetCode篇-深入了解二叉树的经典解法(多种方式实现:构造二叉树)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
   

文章目录

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

        1.1 实现从前序与中序遍历序列来构造二叉树思路   

        1.2 代码实现从前序与中序遍历序列来构造二叉树

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

        2.1 实现从中序与后序遍历序列后遭二叉树思路

        2.2 代码实现从中序与后序遍历序列来构造二叉树

        3.0 根据后缀表达式创建二叉树

        3.1 实现后缀表达式创建二叉树思路

        3.2 代码实现后缀表达式创建二叉树

         4.0 相同的树

        4.1 实现判断两颗树是否相同思路

        4.2 代码实现相同树

         5.0 另一颗树的子树

        5.1 实现判断是否为另一颗树的子树

        5.2 代码实现判断是否为另一颗树的子树


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

题目:

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

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

OJ链接:

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

        1.1 实现从前序与中序遍历序列来构造二叉树思路   

        具体思路为:前序遍历的流程先是访问根节点,到左子树,再到右子树的顺序;中序遍历的流程先是左子树,到访问根节点,再到右子树。因此,在前序的序列中的第一个元素就是该树的根节点的值,然后再通过中序的序列中遍历找根节点。接着就可以将其中序的序列进行拆分,在中序序列中根节点的值的左边部分的序列为左子树,在中序序列中根节点的值的右边部分序列为右子树。又接着将前序序列进行拆分,从索引为 1 开始,长度为:与中序序列中左子树的序列数量是一致的,将这一部分称为左子树;从索引 (1+长度)开始到该序列的结束,将这一部分称为右子树。接下来就是子问题了,通过递归,找到当前节点的左右子树。

       具体例子:前序序列 pre = {3,9,20,15,7},中序序列 in = {9,3,15,20,7} 。先找到该树的节点值 int rootValue = pre[0], TreeNode root = new TreeNode(rootValue),创建该树的根节点。接着对中序序列遍历来找到根节点的值,i == 1 ,在中序序列中找左右子树:在索引在该区间 [0,1)是节点的左子树,而在索引为 [2,5)区间是节点的右子树。在前序序列中找左右子树:在索引为 [1,2)是该节点的左子树,而在索引为 [2,5)区间是节点的右子树。

        子问题:那么现在得到了左子树的前序序列 pre = { 9 } ,左子树的中序序列 in = { 9 },接下来的流程跟以上的流程是一样的,先找到该子树的根节点值 9 ,然后创建一个值为 9 的根节点。接着,遍历中序序列来找到根节点的值,该根节点的左部分就是左子树,该节点的右部分就是右子树。那么这里的左右子树都为空,因此节点为 9 的根节点没有左右子树了。

        往上回溯:来到根节点值为 3 的节点。现在得到了右子树的前序序列 pre = {20,15,7} ,右子树的中序序列 in = {15,20,7} ,接下来的过程是一摸一样的,先找到该子树的根节点值为 20 ,创建值为 20 的根节点。然后在中序序列中进行遍历找到根节点的左右子树 :左子树序列为 {15},右子树序列为 {7},接着在前序序列中找左右子树:左子树序列为 {15},右子树序列为 {7} 。又得到了左右前中序列,按同样的道理继续下去即可,通过上面的结论,当左子树前序 {15} 与左子树中序 {15} 一样时,那么在当前的节点值为 15 的根节点没有左右子树了。同理,当右子树前序 {7} 与右子树中序 {7} 一样时,那么在当前的节点值为 7 的根节点没有左右子树了。

        最后回溯,根节点值为 20 的左子树的根节点为 15,右子树的根节点为 7 ,链接起来,一直回溯到结束返回的最后的节点就是该树的根节点(值为 1 的根节点)。

        需要注意的是:注意左闭右开。因为是同一颗树,在前序中的左右子树的长度跟中序中的左右子树的长度是一样的。

        1.2 代码实现从前序与中序遍历序列来构造二叉树

    //根据前序与中序来构造二叉树
    public TreeNode prevAndInOrderConstructTree(int[] prevOrder, int[] inOrder) {
        if (prevOrder.length == 0) {
            return null;
        }
        int rootValue = prevOrder[0];
        TreeNode root = new TreeNode(rootValue);
        for (int i = 0; i < inOrder.length; i++) {
            if (inOrder[i] == rootValue) {
                int[] inLeft = Arrays.copyOfRange(inOrder,0,i);
                int[] inRight = Arrays.copyOfRange(inOrder,i+1,inOrder.length);

                int[] prevLeft = Arrays.copyOfRange(prevOrder,1,i + 1);
                int[] prevRight = Arrays.copyOfRange(prevOrder,i+1,inOrder.length);

                root.left = prevAndInOrderConstructTree(prevLeft,inLeft);
                root.right = prevAndInOrderConstructTree(prevRight,inRight);
            }
        }
        return root;
    }

        只要某一个序列无论是前序或者是中序序列的长度为 0 ,都代表创建二叉树过程结束了。

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

题目:

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

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

OJ链接:

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

        2.1 实现从中序与后序遍历序列后遭二叉树思路

        具体思路为:中序的序列先访问左子树,再访问根节点,最后到右子树。后序的序列先访问左子树,再访问右子树,最后才到根节点。因此,可以从后序序列中的最后一个元素得到根节点的值,然后利用该值来创建根节点。接着,在中序序列中遍历查找根节点的值,在中序序列中根节点值左边的序列为左子树序列,在中序序列中根节点的右边为右子树的序列。再接着再后序中获取左子树的序列:从索引为 0 开始长度是中序序列得到的左子树的长度;从后序序列中获取右子树序列:除了左子树的序列和根节点值元素,其余就是右子树的序列了。接下来就是子问题了,递归下去,返回当前的根节点。

        具体例子:中序序列 in = {9,3,15,20,7},后序序列 post = {9,15,7,20,3},通过后序得到该树的根节点的值 3,由这个值来创建值为 3 的根节点。接着通过遍历中序序列,找到值为 3 来定位左右子树,左子树的序列为:{9} ,右子树的序列为:{15,20,7} 。再从中序序列中定位左右子树,左子树为:{9},右子树为:{15,7,20} 。现在得到了左右中后序列,中序左子树:{9} ,后序左子树:{9},通过后序得到根节点,再从中序定位该子树的根节点,这里根节点值为 9 的根节点的左右子树均为 null 。接着回溯,返回的该子树的根节点。

        再到右子树中序 {15,20,7} ,右子树后序 {15,7,20},通过后序序列的最后一个值来创建该子树的根节点,在中序中遍历找到该根节点的值,定位该节点的左右子树。中序的左子树序列 {15},右子树序列 {7};后序的左子树序列 {15},后序的右子树序列 {7} 。

        再接着递归,得到左子树中序 {15} ,左子树后序 {15}。通过后序的最后一个元素就是根节点的值来创建该子树的根节点,然后在中序中定位该根节点的左右子树,这里的左右子树都为 null ,返回根节点即可。得到右子树中序 {7},右子树后序 {7},通过后序的最后一个元素为根节点的值来创建该子树的根节点,然后在中序中定位该根节点的左右子树,恰好,这里的左右子树都为 null ,返回根节点即可。

        最后回溯过程,根节点值为 1 的节点的左子树的根节点为 9 的节点,右子树的根节点为 20 的节点。

        2.2 代码实现从中序与后序遍历序列来构造二叉树

    //根据中序与后序来构造二叉树
    public TreeNode inAndPostConstructTree(int[] inOrder, int[] postOrder) {

        if (inOrder.length == 0) {
            return null;
        }
        int rootValue = postOrder[postOrder.length-1];
        TreeNode root = new TreeNode(rootValue);
        for (int j = 0; j < inOrder.length; j++) {
            if (rootValue == inOrder[j]) {
                int[] inLeft = Arrays.copyOfRange(inOrder,0,j);
                int[] inRight = Arrays.copyOfRange(inOrder,j+1,inOrder.length);

                int[] postLeft = Arrays.copyOfRange(postOrder,0,j);
                int[] postRight = Arrays.copyOfRange(postOrder,j,postOrder.length-1);

                root.left = inAndPostConstructTree(inLeft,postLeft);
                root.right = inAndPostConstructTree(inRight,postRight);
            }
        }
        return root;

    }

        需要注意的定位左右子树的序列长度,中序与后序的左右子树都是一一对应的。也因此,当无论任意一个序列结束,都代表着构造二叉树过程结束。

        3.0 根据后缀表达式创建二叉树

        后缀表达式也叫逆波兰表达式,是一种不需要括号的表达式表示方法。在后缀表达式中,运算符总是在操作数的后面,因此可以通过从左到右扫描表达式来创建二叉树。

        3.1 实现后缀表达式创建二叉树思路

        具体思路为:若遇到数字将其压入栈中;若遇到操作符时,将栈中的右左元素弹出栈,操作符节点进行链接弹出来的左右子树,需要注意的是,第一个弹出来的是右操作数,第二个才是左操作数。链接完之后,将其压入栈中即可。循环结束条件为:将后缀表达式遍历完就结束。最后返回栈中的栈顶元素,就是该树的根节点。

        3.2 代码实现后缀表达式创建二叉树

    //根据后缀表达式创建二叉树
    public TreeNode suffixCreateTree(String[] s) {

        LinkedList<TreeNode> stack = new LinkedList<>();
        for (int i = 0; i < s.length; i++) {
            String ch = s[i];
            switch (ch) {
                case "+","-","*","/" -> {
                    TreeNode right = stack.pop();
                    TreeNode left = stack.pop();
                    TreeNode t = new TreeNode(ch);
                    t.right = right;
                    t.left = left;
                    stack.push(t);
                }
                default -> {
                    stack.push(new TreeNode(ch));
                }
            }

        }
        return stack.peek();
    }

         4.0 相同的树

题目:

        给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入:p = [1,2,3], q = [1,2,3]
输出:true

OJ链接:

100. 相同的树

        4.1 实现判断两颗树是否相同思路

        具体思路为:使用递归实现,第一种情况:若一个子树为 null ,另一个子树不为 null ,返回 false ;第二种情况:若两个子树都为 null ,则返回 true 。第三种情况:若两个子树的根节点的值不相同时,返回 false 。子过程,判断完左子树,还需判断右子树。

        4.2 代码实现相同树

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if( p != null && q == null || p ==null && q != null) {
            return false;
        }
        if(p == null && q == null ){
            return true;
        }
        if(p.val != q.val) {
            return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right,q.right);
    }
}

        5.0 另一颗树的子树

题目:

        给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

示例 1:

输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true

OJ链接:

572. 另一棵树的子树

         5.1 实现判断是否为另一颗树的子树

        具体思路为:最根本的问题就是判断是否为相同的树,判断该树的子树与另一颗子树是否相同,不断递归下去,只要相同就返回 true 。直到最后递归结束都没有匹配,则返回 false 。

        5.2 代码实现判断是否为另一颗树的子树

class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(root == null) {
            return false;
        }
        if(isSameTree(root,subRoot)) {
            return true;
        }
        if(isSubtree(root.left,subRoot)) {
            return true;
        }
        if(isSubtree(root.right,subRoot)) {
            return true;
        }
        return false;

    }
        public boolean isSameTree(TreeNode p, TreeNode q) {
        if( p != null && q == null || p ==null && q != null) {
            return false;
        }
        if(p == null && q == null ){
            return true;
        }
        if(p.val != q.val) {
            return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right,q.right);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实用篇 | 一文学会人工智能中API的Flask编写(内含模板)

----------------------- &#x1f388;API 相关直达 &#x1f388;-------------------------- &#x1f680;Gradio: 实用篇 | 关于Gradio快速构建人工智能模型实现界面&#xff0c;你想知道的都在这里-CSDN博客 &#x1f680;Streamlit :实用篇 | 一文快速构建人工智能前端展…

【优选算法系列】【专题二滑动窗口】第三节.904. 水果成篮和438. 找到字符串中所有字母异位词

文章目录 前言一、水果成篮 1.1 题目描述 1.2 题目解析 1.2.1 算法原理 1.2.2 代码编写 1.2.3 题目总结二、找到字符串中所有字母异位词 2.1 题目描述 2.2 题目解析 2.2.1 算法原理 2.2.2 代码编写 …

OpenAI 首席运营官(COO)Brad Lightcap认为商业人工智能被夸大了

美国消费者新闻与商业频道&#xff08;CNBC&#xff09;是美国NBC环球集团持有的全球性财经有线电视卫星新闻台&#xff0c;是全球财经媒体中的佼佼者&#xff0c;其深入的分析和实时报导赢得了全球企业界的信任。在1991年前&#xff0c;使用消费者新闻与商业频道&#xff08;C…

node.js和npm的安装与环境配置(2023最新版)

目录 安装node.js测试是否安装成功测试npm环境配置更改环境变量新建系统变量 安装node.js 1、进入官网下载&#xff1a;node.js官网 我选择的是windows64位的&#xff0c;你可以根据自己的实际情况选择对应的版本。 2、下载完成&#xff0c;安装。 打开安装程序 接受协议 选…

链表OJ—环形链表的约瑟夫问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 世上有两种耀眼的光芒&#xff0c;一种是正在升起的太阳&#xff0c;一种是正在努力学习编程的你!一个爱学编程的人。各位看官&#xff0c;我衷心的希望这篇博客能对你…

操作系统———磁盘调度算法模拟

实验目的 磁盘是可供多个进程共享的设备&#xff0c;当有多个进程都要求访问磁盘是&#xff0c;应采用一种最佳调度算法&#xff0c;以使各进程对磁盘的平均访问时间最小。目前最成用的磁盘调度算法有先来先服务&#xff08;FCFS&#xff09;&#xff0c;最短寻道时间优先&…

增加网站流量的方法

如果您的网站没有获得足够的流量&#xff0c;您可能会错过在线发展业务的重要机会。搜索引擎优化&#xff08;SEO&#xff09;可以帮助提高您网站的知名度&#xff0c;从而吸引更多客户。 SEO的重点是识别高价值的关键词&#xff0c;并将它们整合到网站的内容中&#xff0c;使…

【设计模式-3.2】结构型——适配器模式

说明&#xff1a;本文介绍设计模式中结构型设计模式中的&#xff0c;适配器模式&#xff1b; 插头转换器 适配器模式属于结构型设计模式&#xff0c;设计思想体现在结构上的。以插头转换器为例&#xff0c;当你需要给手机充电&#xff0c;但是眼前只有一个三孔插座&#xff0…

MES管理系统在非标制造企业中的应用

在当今制造业中&#xff0c;非标制造企业逐渐成为一种重要的存在。与传统的批量生产制造企业不同&#xff0c;非标制造企业主要特点是能够根据客户需求进行定制化生产。这种定制化的生产模式对企业的管理提出了更高的要求&#xff0c;同时也带来了更多的挑战。在非标制造企业中…

Emacs之Plantuml用于复杂UML类图(Markdown用于简单类图)(一百三十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

MQTT主题、通配符和最佳实践

MQTT主题在MQTT生态系统非常重要&#xff0c;因为代理&#xff08;broker&#xff09;依赖主题确定哪个客户端接收指定的主题。本文我们将聚集MQTT主题、MQTT通配符&#xff0c;详细讨论使用它们的最佳实践&#xff0c;也会探究SYS主题&#xff0c;提供给代理&#xff08;broke…

超越极限!如何进行高效分布式性能测试,让Jmeter揭示并发下系统的真正实力

一、为什么要进行分布式性能测试 当进行高并发性能测试的时候&#xff0c;受限于Jmeter工具本身和电脑硬件的原因&#xff0c;无法满足我们对大并发性能测试的要求。 基于这种场景下&#xff0c;我们就需要采用分布式的方式来实现我们高并发的性能测试要求。 二、分布式性能测…

深度学习记录--激活函数

激活函数的种类 对于激活函数的选择&#xff0c;通常有以下几种 sigmoid&#xff0c;tanh&#xff0c;ReLU&#xff0c;leaky ReLU 激活函数的选择 之前logistic回归一直使用的激活函数都是sigmoid函数&#xff0c;但一般来说&#xff0c;tanh函数是比sigmoid函数更加好的选…

【小白专用】在 vs 中使用 nuget 安装NPOI

C#操作Excel有多种方法&#xff0c;如通过数据库的方式来读写Excel的OleDb方式&#xff0c;但是OleDb方式需要安装微软office&#xff0c;还可以通过COM组件方式操作Excel&#xff0c;也需要安装微软Excel。如果不想安装微软办公套餐可以使用ClosedXML、EPPlus、NPOI。本文主要…

理解IoC容器初始化

问题&#xff1a;当自己面试或者背诵八股文时&#xff0c;会背到各种各样的spring底层的东西&#xff0c;自己越看越迷糊。 OS&#xff1a;不知道兄弟们是不是也会这样&#xff1f;如果大家没有说明我太菜了。 原因&#xff1a;就是自己学的框架越来越多&#xff0c;很多框架…

线性回归实战

3.1 使用正规方程进行求解 3.1.1 简单线性回归 公式 &#xff1a; y w x b y wx b ywxb 一元一次方程&#xff0c;在机器学习中一元表示一个特征&#xff0c;b表示截距&#xff0c;y表示目标值。 使用代码进行实现&#xff1a; 导入包 import numpy as np import matp…

bc-linux-欧拉重制root密码

最近需要重新安装虚拟机的系统 安装之后发现对方提供的root密码不对&#xff0c;无法进入系统。 上网搜了下发现可以进入单用户模式进行密码修改从而重置root用户密码。 在这个界面下按e键 找到图中部分&#xff0c;把标红的部分删除掉&#xff0c;然后写上rw init/bin/…

JAVEE初阶 多线程基础(七)

懒汉模式 指令重排序问题 一. 懒汉模式的意义和代码实现二. 饿汉模式和懒汉模式的线程安全三. 懒汉模式的线程安全问题解决3.1 加锁阶段3.2 嵌套if阶段3.3 指令重排序问题3.4 解决线程安全问题阶段 一. 懒汉模式的意义和代码实现 在上一章节中,我们先学习了单例模式中的饿汉模式…

【好书推荐】《深入Activiti流程引擎:核心原理与高阶实战》

学习工作流&#xff0c;推荐贺老师的书《深入Activiti流程引擎&#xff1a;核心原理与高阶实战》&#xff0c;对系统学习和深入掌握Activiti/Flowable流程引擎的用法非常有帮助。 图书链接

我的NPI项目之Android电源系列 -- 关于剩余充满时间的问题(一)

我的新项目是基于高通最新的5G平台&#xff0c;但是由于还没有拿到EVT。所以&#xff0c;就在目旧的平台和OS上进行学习。遇到第一个问题就是插上type-c之后&#xff0c;充满剩余时间异常的问题。 问题描述&#xff0c;在充电过程中&#xff0c;显示充满时间为“0 min left unt…
最新文章