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

news2024/9/28 13:24:52

二叉树里的双指针

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

判断两棵树是否相同

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;
}

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

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

相关文章

004-Zynq实现SD卡存储灰度图片(彩色图片存储正点已开源)

文章目录 前言一、为什么参考ov7725照相机实验存储不了灰度&#xff1f;二、SD卡实现步骤1.配置Zynq核中的SD卡接口2.PS端勾选xilffs3.PS端代码4.读卡器读取SD卡结果呈现 总结 前言 最近在弄SD卡存储灰度图片&#xff0c;参考了正点原子的OV7725照相机实验&#xff0c;但发现最…

网安入门09-Sql注入(绕过方法梳理)

ByPass SQL注入ByPass是指攻击者通过各种手段绕过应用程序中已经实施的SQL注入防御措施&#xff0c;例如输入恶意数据、修改请求头等方式&#xff0c;绕过过滤、转义、限制等操作&#xff0c;从而成功地执行恶意SQL语句。攻击者使用SQL注入ByPass技术可以让应用程序的防御措施…

为什么Java中“1000==1000”为false,而”100==100“为true?

大家好&#xff0c;我是可乐。 在日常编程中&#xff0c;我们经常遇到一些看似简单却隐藏着复杂逻辑的问题。 比如&#xff0c;你是否想过为什么在 Java 中表达式10001000会返回 false&#xff0c;而 100100 却返回 true 呢&#xff1f; Integer a 100; Integer b 100; Sy…

王国维的人生三境界,这一生至少当一次傻瓜

一、人生三境界 古今之成大事业、大学问者&#xff0c;必经过三种之境界。“昨夜西风凋碧树&#xff0c;独上高楼&#xff0c;望尽天涯路。”此第一境也。“衣带渐宽终不悔&#xff0c;为伊消得人憔悴。”此第二境也。“众里寻他千百度&#xff0c;蓦然回首&#xff0c;那人却…

双向数据绑定详细解析(超详细)

文章目录 一、什么是双向绑定二、双向绑定的原理是什么理解ViewModel 三、实现双向绑定实现编译Compile依赖收集 参考文献 一、什么是双向绑定 我们先从单向绑定切入单向绑定非常简单&#xff0c;就是把Model绑定到View&#xff0c;当我们用JavaScript代码更新Model时&#xf…

永磁同步电机的磁场定向控制

目录 概述 通过系统仿真验证行为 探索模型架构 生成用于集成到嵌入式应用程序的控制器 C 代码 指定控制器模型的参考行为 创建 PIL 实现 准备用于 PIL 测试的控制器模型 测试生成的代码的行为和执行时间 结论 此示例说明从电机控制算法生成 C 代码并验证其编译行为和执…

AcWing 998. 起床困难综合症

原题链接 其实上面这一堆就是想说&#xff0c;输入 n,m以及 n 个数和该数所对应的运算&#xff0c;其中运算包括有 与、或、异或 三种&#xff0c;真正的问题就是在所有不大于 m 的数&#xff08;非负数&#xff09;中&#xff0c;对给定的 n 个数都按该数所对应的运算运算一遍…

GFS论文解读(一)——设计概述

介绍 在当今大数据时代&#xff0c;分布式文件系统已经成为处理海量数据的重要工具。而在这个领域中&#xff0c;「GFS&#xff08;Google File System&#xff09;」论文无疑是一篇具有里程碑意义的文献。GFS 由 Google 公司发表于 2003 年&#xff0c;它介绍了 Google 公司内…

C练习——肇事卡车车牌号

题目&#xff1a; 一辆卡车违反交通规则&#xff0c;撞人后逃跑。现场有3人目击事件&#xff0c;但没有记住车牌号&#xff0c;只记住了车号的一些特征。 甲说&#xff1a;“牌照前两位数字是相同的”&#xff0c;乙说&#xff1a;“牌照的后两位数字是相同的&#xff0c;但与…

centos通过yum安装redis

1. 安装yum添加epel源(此步根据环境&#xff0c;如果有源则可跳过&#xff0c;在阿里去可跳过&#xff09; yum install epel-release 2 使用yum安装Redis yum install redis 出现如下图所示的内容 3 Redis配置 vim /etc/redis.conf :set number(显示行号) 61行&#x…

Flume基础知识(十一):Flume自定义接口

1&#xff09;案例需求 使用 Flume 采集服务器本地日志&#xff0c;需要按照日志类型的不同&#xff0c;将不同种类的日志发往不同的分析系统。 2&#xff09;需求分析 在实际的开发中&#xff0c;一台服务器产生的日志类型可能有很多种&#xff0c;不同类型的日志可能需要 发送…

在群晖NAS上搭建私有部署笔记软件——Blossom

一、Blossom 简介 Blossom 是一个需要私有部署的笔记软件&#xff0c;虽然本身定位是一个云端软件&#xff0c;但你仍然可以在本地部署&#xff0c;数据和图片都将保存在本地&#xff0c;不依赖任何的图床或者对象存储。 Blossom | Blossom (wangyunf.com)https://www.wangyun…

Zoho SalesIQ:提高品牌在社交媒体上参与度的实用指南

在当今快节奏的数字世界中&#xff0c;品牌参与度变得比以往任何时候都更加重要。社交媒体在企业与客户互动方面发挥着至关重要的作用&#xff0c;了解如何很好地利用社交媒体来增强品牌参与度至关重要。 正如我们在之前的博客中所了解到的&#xff0c;品牌参与是指在品牌与其…

企业数据库安全管理规范

1.目的 为规范数据库系统安全使用活动&#xff0c;降低因使用不当而带来的安全风险&#xff0c;保障数据库系统及相关应用系统的安全&#xff0c;特制定本数据库安全管理规范。 2.适用范围 本规范中所定义的数据管理内容&#xff0c;特指存放在信息系统数据库中的数据。 本…

原文件名自动编号方法:简单易懂的文件重命名规则

当处理大量文件时&#xff0c;例如图片、文档或其他类型的文件&#xff0c;经常要给它们重新命名&#xff0c;以便于管理和查找。一种常见的方法是使用自动编号&#xff0c;这样可以从001开始&#xff0c;一直编号到最后的文件。这种方法既简单又高效&#xff0c;且易于理解。下…

【读书】《白帽子讲web安全》个人笔记Ⅱ-1

目录 第二篇 客户端脚本安全 第2章 浏览器安全 2.1同源策略 2.2浏览器沙箱 2.3恶意网址拦截 2.4高速发展的浏览器安全 第二篇 客户端脚本安全 第2章 浏览器安全 近年来随着互联网的发展&#xff0c;人们发现浏览器才是互联网最大的入口&#xff0c;绝大多数用户使用互联…

UV机-理光G5六彩一白一光油配置

UV机-理光G5六彩一白一光油配置

【Navigation】global_planner 源码解析

全局规划器 global_planner 功能包 文章目录 global_planner 功能包结构1、plan_node.cpp2、planner_core.cpp3、astar.cpp4、dijkstra.cpp5、quadratic_calculator.cpp6、grid_path.cpp7、gradient_path.cpp8、orientation_filter.cpp全局规划大都基于静态地图进行规划,产生路…

加密的手机号如何模糊查询?

1 一次加载到内存 实现这个功能&#xff0c;我们第一个想到的办法可能是&#xff1a;把个人隐私数据一次性加载到内存中缓存起来&#xff0c;然后在内存中先解密&#xff0c;然后在代码中实现模糊搜索的功能。 这样做的好处是&#xff1a;实现起来比较简单&#xff0c;成本非常…

约数个数和约数之和算法总结

知识概览 约数个数 由算数基本定理 可得对于N的任何一个约数d&#xff0c;有 因为N的每一个约数和~的一种选法是一一对应的&#xff0c;根据乘法原理可得&#xff0c; 一个数的约数个数为 约数之和 一个数的约数之和公式为 多项式乘积的每一项为 正好对应的是一个数的每一个约…