二叉树的经典算法(算法村第八关青铜挑战)

二叉树里的双指针

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

判断两棵树是否相同

100. 相同的树 - 力扣(LeetCode)

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

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

示例 1:

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

示例 2:

img
输入:p = [1,2], q = [1,null,2]
输出:false

示例 3:

img
输入:p = [1,2,1], q = [1,1,2]
输出:false

提示:

  • 两棵树上的节点数目都在范围 [0, 100]
  • -104 <= Node.val <= 104
同时进行前序遍历

两个二叉树同时进行前序遍历,先判断根节点是否相同, 如果相同再分别判断左右子树是否相同,判断的过程中只要有一个不相同就返回 false,全部相同才会返回true。

public boolean isSameTree(TreeNode p, TreeNode q)
{
    //如果结点都为null,则认为两个结点相同
    if(p == null && q == null)
        return true;

    //经过前面的判断,到这里要么p、q都不为null,要么p、q中只有一个为null
    //若p、q一个为null一个不为null,则认为两棵树不相同
    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);
}

对称二叉树

101. 对称二叉树 - 力扣(LeetCode)

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

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

示例 2:

img
输入:root = [1,2,2,null,3,null,3]
输出:false

提示:

  • 树中节点数目在范围 [1, 1000]
  • -100 <= Node.val <= 100
递归

若根结点的左右节点都不为 null 且 val 相同。

  1. 比较外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
  2. 比较内侧是否对称:传入的是左节点的右孩子,右节点的左孩子。
  3. 有一侧不对称就返回false ,左右都对称则返回true 。
//主方法
public boolean isSymmetric(TreeNode root)
{
    return check(root.left, root.right);
}

public boolean check(TreeNode p, TreeNode q)
{
    if (q == null && p == null)
        return true;

    if(q == null || p == null)
        return false;

    if(q.val != p.val)
        return false;

    return check(p.left ,q.right) && check(p.right, q.left);
}

合并二叉树

617. 合并二叉树 - 力扣(LeetCode)

给你两棵二叉树: root1root2

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。

合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意: 合并过程必须从两个树的根节点开始。

示例 1:

img

输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]

提示:

  • 两棵树中的节点数目在范围 [0, 2000]
  • -104 <= Node.val <= 104

合并得到某个节点之后,还要对该节点的左右子树分别进行合并

public TreeNode mergeTrees(TreeNode root1, TreeNode root2)
{
    //触底时返回 null 或者不为 null 的那个结点
    if (root1 == null)
        return root2;

    if (root2 == null)
        return root1;

    //两个结点均不为 null 则进行合并,生成一个新结点(显性合并)
    TreeNode mergeNode = new TreeNode(root1.val + root2.val);

    //合并两个结点的左子树,然后衔接到新结点上
    mergeNode.left = mergeTrees(root1.left, root2.left);
    //合并两个结点的右子树,然后衔接到新结点上
    mergeNode.right = mergeTrees(root1.right, root2.right);

    return mergeNode;   //返回合并后的新结点
}

路径专题

二叉树的所有路径

257. 二叉树的所有路径 - 力扣(LeetCode)

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

示例 1:

img
输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

示例 2:

输入:root = [1]
输出:["1"]

提示:

  • 树中节点的数目在范围 [1, 100]
前序遍历+ list

调整一下对结点的处理操作即可

public List<String> binaryTreePaths(TreeNode root)
{
    ArrayList<String> list = new ArrayList<>();
    preOrder(root, list, "");
    return list;
}

public void preOrder(TreeNode root, List<String> list, String ans)
{
    if (root == null)
        return;

    //找到一个叶子结点后,将路径添加到列表,返回
    if (root.left == null && root.right == null)
    {
        ans = ans + String.format("%s",root.val);
        list.add(ans);
        return;
    }

    //保存路径上的节点
    ans = ans + String.format("%s->",root.val);

    preOrder(root.left, list, ans);
    preOrder(root.right, list, ans);
}

二叉树的路径总和

112. 路径总和 - 力扣(LeetCode)

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false

叶子节点 是指没有子节点的节点。

示例 1:

img
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

提示:

  • 树中节点的数目在范围 [0, 5000]
前序遍历 + list

本来想着用一个 boolean 类型的 flag 标记一下即可,但发现即使找到答案路径后,flag 在往后的递归中也会受到影响而改变结果,于是不得已用了一个列表来标记(似乎列表才属于循环不变量、递归不变量)

public boolean hasPathSum(TreeNode root, int targetSum)
{
    ArrayList<Boolean> list = new ArrayList<Boolean>();
    preOrder(root,list,0, targetSum);

    return !list.isEmpty();
}

public void preOrder(TreeNode root, ArrayList<Boolean> list, int sum, int targetSum)
{
    if (root == null)
        return;

    //在叶子结点处进行判断,然后返回
    if (root.left == null && root.right == null)
    {
        sum = sum + root.val;

        if(sum ==  targetSum)
            list.add(true);
        return;
    }

    //计算路径上非叶节点的和
    sum = sum + root.val;

    preOrder(root.left, list, sum, targetSum );
    preOrder(root.right, list, sum, targetSum);
}
递归地询问子节点是否满足条件

若当前节点就是叶子节点,则直接判断 val 是否等于 targetSum;若当前节点不是叶子节点,则递归地询问它的两个子节点是否满足条件 val == targetSum - 父节点.val ,有一个满足即返回 true,两个都不满足则返回 false (子节点为 null 视为不满足)。

public boolean hasPathSum(TreeNode root, int targetSum)
{
    if (root == null)
        return false;

    if (root.left == null && root.right == null)
        return root.val == targetSum;

    boolean left = hasPathSum(root.left, targetSum - root.val);
    boolean right = hasPathSum(root.right, targetSum - root.val);

    return left || right;
}

翻转二叉树

226. 翻转二叉树 - 力扣(LeetCode)

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:

img

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
递归

递归地翻转当前结点的两个子节点即可

public TreeNode invertTree(TreeNode root)
{
    if (root == null)
        return null;

    TreeNode t = root.left;
    root.left = root.right;
    root.right = t;

    invertTree(root.left);
    invertTree(root.right);

    return root;
}
层次遍历
public TreeNode invertTree(TreeNode root)
{
    if (root == null)
        return null;

    ArrayDeque<TreeNode> queue = new ArrayDeque<>();
    queue.offer(root);

    while (!queue.isEmpty())
    {
        TreeNode curNode = queue.poll();

        TreeNode t = curNode.left;
        curNode.left = curNode.right;
        curNode.right = t;

        if (curNode.left != null)
            queue.offer(curNode.left);
        if (curNode.right != null)
            queue.offer(curNode.right);
    }

    return root;
}

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

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

相关文章

1- forecasting at scale论文阅读

目录 1. 什么是时间序列2. 什么是时间序列预测3. 时间序列预测的范式4. 时间序列的专有名词介绍5. 时间序列评估 1. 什么是时间序列 按时间先后顺序出现的有序序列 2. 什么是时间序列预测 点预测&#xff1a;预测未来的某一个时间点&#xff0c;它的值到底是多少&#xff0c…

高效管理文件方法:每4个文件前面加序号,4个文件后面又单独编号技巧

在日常工作中&#xff0c;文件管理是一项常见的任务。要更高效地管理文件&#xff0c;可以通过在每个文件前面加序号&#xff0c;并在每个序号对应的文件后面进行单独编号的方法来实现。这种方法有助于快速找到所需文件&#xff0c;也能提高工作效率。下面一起来看下云炫文件管…

K8S部署的pod一直处于Pending状态问题解决

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

初识动态内存管理

前言&#xff1a; 我们都知道&#xff0c;内存分为几个区——栈区、堆区、静态区、常量区、代码区&#xff0c;我们在写代码的时候经常会遇到栈溢出这个问题&#xff0c;是因为在程序运行之前&#xff0c;我们无法准确的知道要分配多少空间给程序&#xff0c;所以说很容易造…

监控API的指标

监控服务器已经是常态了&#xff0c;但是监控API的表现是啥意思呢&#xff1f;还有监控指标&#xff1f;今天就来看看如何监控API。 正如监控应用程序以确保高质量性能一样&#xff0c;也必须监控API。 API是应用程序相互通信的管道。更具体地说&#xff0c;API提供了一种方法…

【算法每日一练]-图论(保姆级教程篇14 )#会议(模板题) #医院设置 #虫洞 #无序字母对 #旅行计划 #最优贸易

目录 今日知识点&#xff1a; 求数的重心先dfs出d[1]和cnt[i]&#xff0c;然后从1进行dp求解所有d[i] 两两点配对的建图方式&#xff0c;检查是否有环 无向图欧拉路径路径输出 topodp求以i为终点的游览城市数 建立分层图转化盈利问题成求最长路 会议&#xff08;模板题&a…

向日葵远程工具安装Mysql5.7的安装与配置

文章目录 一、向日葵远程工具安装二、Mysql5.7的安装与配置2.1解压2.2再把my.ini文件放入解压后的文件里面2.3.改变my.ini文件2.4.用管理员身份运行cmd&#xff0c;进入bin文件夹里&#xff0c;运行"mysqld install"命令&#xff0c;出现以下就说明成功了2.5.注册完s…

20. Mysql 游标的定义和使用

文章目录 概念游标的基本语法声明游标打开游标使用游标关闭游标精选示例 总结 概念 游标&#xff08;Cursor&#xff09;是一种数据库对象&#xff0c;可以指向存储在数据库表中的数据行指针。用于在 sql 语句的执行过程中&#xff0c;通过对查询结果集进行逐行的操作和访问。…

【大数据进阶第三阶段之Hive学习笔记】Hive常用命令和属性配置

目录 1、Hive安装 2、HiveJDBC访问 2.1、启动hiveserver2服务 2.2、连接hiveserver2服务 2.3、注意 3、Hive常用交互命令 3.1、“-e”不进入hive的交互窗口执行sql语句 3.2、“-f”执行脚本中sql语句 4、Hive其他命令操作 4.1、退出hive窗口 4.2、在hive cli命令窗口…

rosbag 源码阅读笔记-1

这篇文字想通过在自己的机器上查找rosbag的源码在哪里&#xff08;而不是通过google搜索&#xff09;&#xff0c;来和大家分享一些ros和python的常用命令&#xff0c;了解一下rosbag的调用过程。 怎么查到源码在哪里 当然我们可以直接上ros的官网去查看&#xff0c;路径在这…

静态网页设计——科学家网(HTML+CSS+JavaScript)(dw、sublime Text、webstorm、HBuilder X)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a;https://www.bilibili.com/video/BV1wg4y1Q7qm/?vd_source5f425e0074a7f92921f53ab87712357b 源码&#xff1a;https://space.bilibili.com…

基于Springboot的在线考试系统

点击以下链接获取源码&#xff1a; https://download.csdn.net/download/qq_64505944/88499371 mysql5、mysql8都可使用 内含配置教程文档&#xff0c;一步一步配置 Springboot所写 管理员页面 学生页面

jetson deepstream 解码接入编码输出

不需要编解码输出画面的直接到7 使用就行 1 jetson主板编译工具 在jetson主板上安装gstreamer工具链&#xff0c;编译opencv sudo apt install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-ugly gstreamer1.0-rtsp python3-dev pytho…

【信息论与编码】习题-判断题-第三部分

目录 判断题48. 利用状态极限稳态分布概率和符号的状态一步转移概率来求m阶马尔可夫信源的极限熵。49. 连续信源或模拟信号的信源编码的理论基础是限失真信源编码定理 。50. 具有一一对应关系的无噪信道的信道容量CH(X)。51. 在游程编码过程中&#xff0c;“0”游程和“1”游程…

PHP进阶-实现网站的QQ授权登录

授权登录是站点开发常见的应用场景&#xff0c;通过社交媒体一键授权可以跳过注册站点账户的繁琐操作。本文将讲解如何用PHP实现QQ授权登录。首先&#xff0c;我们需要申请QQ互联开发者账号获得APPID和密钥&#xff1b;接着&#xff0c;我们下载QQ官方SDK&#xff1a;PHP SDK v…

【VTKExamples::Visualization】第一期 Arbitrary3DCursor

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ&#xff1a;870202403 前言 本文分享Example中Visualization模块中的Arbitrary3DCursor样例&#xff0c;主要解析vtkProbefileter&#xff0c;希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴的点赞关注&#xff0c;小易会…

大学物理实验重点——霍尔效应

霍尔系数 霍尔元件灵敏度&#xff0c;愈大愈好 负效应&#xff1a; 1. 不等位电势 V0&#xff1a;两个霍尔电极不可能绝对对 称地焊在霍尔元件两侧&#xff08;图 2&#xff09;、霍尔元件电阻率不均匀、工作电极的端面接触不良都 可能造成 C、D 两极不处在同一等位面上。R0 确…

XCTF:凯撒大帝在培根里藏了什么[WriteUP]

密文&#xff1a; ABBABAABBAAAAABABABAABABBAAAAABAABBAAABAABBBABBAABABBABABAAABABBBAABAABABABBBAABBABAA 根据题目提示&#xff0c;应该有两种加密算法 1.培根加密 2.凯撒加密 根据语境&#xff0c;且密文与凯撒加密后的密文不符合&#xff0c;先尝试培根解密 培根解…

大数据时代必备技能!Shell脚本学习网站助你一臂之力!

介绍&#xff1a;Shell脚本是一种用于自动化任务的脚本语言&#xff0c;它使用Shell命令来执行一系列操作。Shell脚本通常以.sh为扩展名&#xff0c;并使用#!/bin/bash作为第一行来指定使用的Shell解释器。 在Shell脚本中&#xff0c;我们可以使用各种命令和控制结构来实现自动…

Hadolint:Lint Dockerfile 的完整指南

想学习如何使用 Hadolint 对 Dockerfile 进行 lint 处理吗&#xff1f;这篇博文将向您展示如何操作。这是关于 Dockerfile linting 的完整指南。 通过对 Dockerfile 进行 lint 检查&#xff0c;您可以及早发现错误和问题&#xff0c;并确保它们遵循最佳实践。 什么是Hadolint…