算法通关村第八关—二叉树的经典算法题(青铜)

news2024/12/25 23:47:49

         二叉树的经典算法题

一、二叉树里的双指针

 双指针就是定义了两个变量,在二叉树中有时候也需要至少定义两个变量才能解决问题,这两个指针可能针对一棵树,也可能针对两棵树,姑且也称之为“双指针”吧。一般是与对称、反转和合并等类型相关。

1.1 判断两棵树是否相同

 LeetCode100:给你两棵二叉树的根节点p和q,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
image.png
image.png
image.png
image.png
 可以对两个二叉树同时进行前序遍历,先判断根节点是否相同,如果相同再分别判断左右子节点是否相同,判断的过程中只要有一个不相同就返回false,如果全部相同才会返回true。
注意返回true的条件是p,q结点同时为null
代码如下:

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

1.2 对称二叉树

LeetCode101给定一个二叉树,检查它是否是镜像对称的。
image.pngtrue image.pngfalse
 因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。这里的关键还是如何比较和如何处理结束条件。
 单层递归的逻辑就是处理左右节点都不为空,且数值相同的情况。
1.比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
2.比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
3.如果左右都对称就返回true,有一侧不对称就返回false。

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

1.3 合并二叉树

 LeetCod617.给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为NULL的节点将直接作为新二叉树的节点。
image.png
 两个二叉树的对应节点可能存在以下三种情况,对于每种情况使用不同的合并方式。
1.如果两个二叉树的对应节点都为空,则合并后的二叉树的对应节点也为空;
2.如果两个二叉树的对应节点只有一个为空,则合并后的二叉树的对应节点为其中的非空节点;
3.如果两个二叉树的对应节点都不为空,则合并后的二叉树的对应节点的值为两个二叉树的对应节点的值之和,此时需要显性合并两个节点。
 对一个节点进行合并之后,还要对该节点的左右子树分别进行合并。
代码如下:

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1 == null) return root2;
        if(root2 == null) return root1;
        TreeNode merge = new TreeNode(root1.val + root2.val);
        merge.left = mergeTrees(root1.left,  root2.left);
        merge.right = mergeTrees(root1.right,  root2.right);
        return merge;
    }
}

二、路径专题

2.1 二叉树的所有路径

 LeetCode257:给你一个二叉树的根节点root,按任意顺序,返回所有从根节点到叶子节点的路径。叶子节点是指没有子节点的节点。
image.png
image.png
 我们可以注意到有几个叶子节点,就有几条路径,那如何找叶子节点呢?我们知道深度优先搜索就是从根节点开始一直找到叶子结点,我们这里可以先判断当前节点是不是叶子结点,再决定是不是向下走,如果是叶子结点,我们就增加一条路径,就像下面图中这样:
image.png
 这里还有个问题,当得到一个叶子结点容易,那这时候怎么知道它所在的完整路径是什么呢?例如上图中得到D之后,怎么知道其前面的A和B呢?简单,增加一个String类型的变量中,访问每个节点访问的时候先存到String中,到叶子节点的时候再添加到集合里:

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> list = new ArrayList();
        dfs(root,"",list);
        return list;
    }
    public void dfs(TreeNode root, String path, List<String> list){
        if(root == null) return;
        if(root.left == null && root.right == null){
            list.add(path + root.val);
            return;
        }
        dfs(root.left, path + root.val +"->",list);
        dfs(root.right, path + root.val +"->",list);
    }
}

2.2 路径总和

 上面我们讨论的找所有路径的方法,那我们是否可以再找一下哪条路径的和为目标值呢?
 LeetCode112题:给你二叉树的根节点root和一个表示目标和的整数targetSum,判断该树中是否存在根节点到子节点的路径,这条路径上所有节点值相加等于目标和targetSum。叶子节点是指没有子节点的节点。
image.png
image.png
image.png
 本题询问是否存在从当前节点root到叶子节点的路径,满足其路径和为sum,假定从根节点到当前节点的值之和为val,我们可以将这个大问题转化为一个小问题:是否存在从当前节点的子节点到叶子的路径,满足其路径和为sum-val。
 不难发现这满足递归的性质,若当前节点就是叶子节点,那么我们直接判断sum是否等于val即可(因为路径和已经确定,就是当前节点的值,我们只需要判断该路径和是否满足条件)。若当前节点不是叶子节点,我们只需要递归地询问它的子节点是否能满足条件即可。
讲义代码如下

public boolean hasPathSum(TreeNode root,int sum){
    if(root == null) return false;
    if (root.left == null && root.right == null)
        return sum ==root.val;

    boolean left = hasPathSum(root.left,sum, root.val);
    boolean right=hasPathSum(root.right,sum, root.val);
    return left || right;
}

自己根据上一道题写的

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        Set<Integer> set = new HashSet();
        int sum = 0;
        dfs(root, set, sum);
        if(set.contains(targetSum)) return true;
        return false;

    }
    public void dfs(TreeNode root, Set<Integer> set, int sum){
        if(root == null){
            return;
        }
        if(root.left == null && root.right == null){
            sum += root.val;
            set.add(sum);
            return;
        }
        sum += root.val;
        dfs(root.left, set, sum);
        dfs(root.right, set, sum);
    }
}

三、翻转的妙用

LeetCode:226翻转二叉树,将二叉树整体反转。如下图所示:
image.png
 根据上图,可以发现想要翻转树,就是把每一个节点的左右孩子交换一下。关键在于遍历顺序,前中后序应该选哪一种遍历顺序?遍历的过程中去翻转每一个节点的左右孩子就可以达到整体翻转的效果。注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果。
 这是一道很经典的二叉树问题。显然,我们从根节点开始,递归地对树进行遍历,并从叶子节点先开始翻转。如果当前遍历到的节点oot的左右两棵子树都已经翻转,那么我们只需要交换两棵子树的位置,即可完成以root为根节点的整棵子树的翻转。
先看前序交换:

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return root;
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

后序交换

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return root;
        invertTree(root.left);
        invertTree(root.right);
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        return root;
    }
}

 这道题目使用前序遍历和后序遍历都可以,主要区别是:前序是先处理当前节点再处理子节点,是自顶向下;后序是先处理子结点最后处理自己,一个是自下而上的。观察下图就明白了:
image.png
 本题还可以使用层次遍历实现,核心思想是元素出队时,先将其左右两个孩子不是直接入队,而是先反转再放进去,代码如下:

class Solution{
    public TreeNode invertTree(TreeNode root){
        if(root == null) return null;
        //将二叉树中的节点逐层放入队列中,再迭代处理队列中的元素
        Qeque<TreeNode> queue = new LinkedList();
        queue.add() = root;
        while (queue.size() > 0){
            //每次都从队列中拿一个节点,并交换这个节点的左右子树
            TreeNode node = queue.remove();
            TreeNode temp = node.left;
            node.left = node.right;
            node.right = temp;
            //如果当前节点的左子树不为空,则放入队列等待后续处理
            if (node.left != null) queue.add(node.left);
            //如果当前节点的右子树不为空,则放入队列等待后续处理
            if (node.right != null) queue.add(node.right);
        }
        return root;
    }

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

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

相关文章

6-6 计算最长的字符串长度

本题要求实现一个函数&#xff0c;用于计算有n个元素的指针数组s中最长的字符串的长度。 函数接口定义&#xff1a; int max_len( char *s[], int n ); 其中n个字符串存储在s[]中&#xff0c;函数max_len应返回其中最长字符串的长度。 裁判测试程序样例&#xff1a; #incl…

algorithm graphics

绘制地图坐标路线_哔哩哔哩_bilibili neo4j test-CSDN博客

【Angular开发】Angular 16发布:发现前7大功能

Angular 于2023年5月3日发布了主要版本升级版Angular 16。作为一名Angular开发人员&#xff0c;我发现这次升级很有趣&#xff0c;因为与以前的版本相比有一些显著的改进。 因此&#xff0c;在本文中&#xff0c;我将讨论Angular 16的前7个特性&#xff0c;以便您更好地理解。…

关于 SAP S/4HANA 中的控制您应该了解什么-Part1

原文地址&#xff1a;What you should know about controlling in SAP S/4HANA. (Part 1) | SAP Blogs &#xff08;自 SAP S/4HANA 版本 1909 起更新&#xff09; 作为一名CO顾问&#xff0c;我对 SAP ERP 中央组件 (ECC) 向 SAP S/4HANA 的演变感到非常兴奋。 自从第一个版…

13.触发器

目录 1、创建触发器 1、创建只有一个执行语句的触发器 2、创建有多个执行语句的触发器 2、查看触发器 1、通过SHOW TRIGGERS查看触发器: 2.在triggers 表中查看触发器信息 3、使用触发器 4、删除触发器 1、创建触发器 MySQL 的触发器和存储过程一样&#xff0c;都是嵌…

高效扫频阻垢装置广谱感应水处理设备介绍工作原理使用参数和选型

​ 1&#xff1a;高效扫频阻垢装置设备介绍 高效扫频阻垢装置是一种通过控制箱释放变频电磁信号&#xff0c;传输到信号放大装置&#xff0c;管道外侧的电磁线圈和电锤产生高频机械振动&#xff0c;在管道和水中传输&#xff0c;通过共振机理破坏水分子之间的氢键&#xff0c;产…

ubuntu 命令行安装 conda

安装包地址&#xff1a; Index of / 找到对应的版本&#xff0c;右键点复制链接 wget https://repo.anaconda.com/archive/Anaconda3-2023.09-0-Linux-x86_64.shbash Anaconda3-2023.09-0-Linux-x86_64.sh https://linzhji.blog.csdn.net/article/details/126530244

计算机病毒判定专家系统原理与设计《文字提取人工修正》

内容源于网络。网络上流转的版本实在是不易阅读&#xff0c; 又不忍神作被糟蹋故稍作整理&#xff0c;对于内容仍然有识别不准的地方&#xff0c;网友可留言&#xff0c;我跟进修改。 雷 军 &#xff08;武汉大学计算机系&#xff0c;430072) 摘要: 本文详细地描述了…

Excel表格转换word的两个方法

Excel表格想要转换到word文档中&#xff0c;直接粘贴复制的话&#xff0c;可能会导致表格格式错乱&#xff0c;那么如何转换才能够保证表格不错乱&#xff1f;今天分享两个方法&#xff0c;excel表格转换为word文件。 方法一&#xff1a; 首先打开excel表格&#xff0c;将表格…

Linux部署Kettle(pentaho-server-ce-9.4.0.0-343)记录/配置MySQL存储

下载地址 Kettle 是一个开源的数据集成工具&#xff0c;它是 Pentaho Data Integration&#xff08;PDI&#xff09;项目的一部分。要访问 Kettle 的官方网站&#xff0c;可以通过访问其母公司 Hitachi Vantara 的网站来找到相关信息 官方网站&#xff1a;https://www.hitachi…

面试 JVM 八股文五问五答第一期

面试 JVM 八股文五问五答第一期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1.JVM内存布局 Heap (堆区&#xff09; 堆是 OOM 故障最主要的发生区域。它是内存…

class074 背包dp-分组背包、完全背包【算法】

class074 背包dp-分组背包、完全背包【算法】 算法讲解074【必备】背包dp-分组背包、完全背包 code1 P1757 通天之分组背包 // 分组背包(模版) // 给定一个正数m表示背包的容量&#xff0c;有n个货物可供挑选 // 每个货物有自己的体积(容量消耗)、价值(获得收益)、组号(分组)…

分布式搜索引擎02

分布式搜索引擎02 在昨天的学习中&#xff0c;我们已经导入了大量数据到elasticsearch中&#xff0c;实现了elasticsearch的数据存储功能。但elasticsearch最擅长的还是搜索和数据分析。 所以今天&#xff0c;我们研究下elasticsearch的数据搜索功能。我们会分别使用DSL和Res…

小红书笔记种草表现怎么看,营销攻略!

小红书平台的传播&#xff0c;离不开内容种草。当我们撰写好一篇笔记并进行发布后&#xff0c;该如何衡量这篇笔记的种草表现呢?今天我们为大家分享下小红书笔记种草表现怎么看&#xff0c;营销攻略&#xff01; 一、小红书笔记种草衡量指标 想要了解小红书笔记种草表现怎么看…

智能监控平台/视频共享融合系统EasyCVR接入大华SDK后只有一路通道可云台控制该如何解决?

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…

31、卷积 - 参数 dilation 以及空洞卷积

在卷积算法中,还有一个不常见的参数叫做dilation(中文:膨胀)。 很多同学可能没听说过这个参数,下面看看这个参数有什么作用,用来控制什么的。 我们还是放这个经典的卷积运算图,图中是看不出 dilation 这个参数的存在的。 如果再换一张图呢,发现两图的区别了吗? 没错…

蓝桥杯Web组学习总结 - 目录导航版

HTML5 HTML 基础标签 HTML5 标签列表 HTML5 新特性 HTML5都有哪些新特性&#xff1f; CSS3 CSS 基础语法 CSS参考手册 盒子模型 CSS Box Model (盒子模型) 浮动与定位&#xff1f;&#xff1f; CSS 浮动(float)与定位(position) CSS布局之浮动和定位 CSS3 新特性 …

Python从入门到精通五:Python数据容器

数据容器入门 为什么学习数据容器 思考一个问题&#xff1a;如果我想要在程序中&#xff0c;记录5名学生的信息&#xff0c;如姓名。 如何做呢&#xff1f; 学习数据容器&#xff0c;就是为了批量存储或批量使用多份数据 Python中的数据容器&#xff1a; 一种可以容纳多份…

【每日一题】—— B. StORage room(Codeforces Round 912 (Div. 2))(位操作符)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

LeetCode5.最长回文子串

昨天和之前打比赛的队友聊天&#xff0c;他说他面百度面到这道算法题&#xff0c;然后他用暴力法解的&#xff0c;面试官让他优化他没优化出来&#xff0c;这道题我之前没写过&#xff0c;我就想看看我能不能用效率高一点的方法把它做出来&#xff0c;我一开始就在想用递归或者…