[Lc_2 二叉树dfs] 布尔二叉树的值 | 根节点到叶节点数字之和 | 二叉树剪枝

news2025/4/22 0:58:57

目录

1.计算布尔二叉树的值

题解

2.求根节点到叶节点数字之和

3. 二叉树剪枝

题解


1.计算布尔二叉树的值

链接:2331. 计算布尔二叉树的值

给你一棵 完整二叉树 的根,这棵树有以下特征:

  • 叶子节点 要么值为 0 要么值为 1 ,其中 0 表示 False1 表示 True
  • 非叶子节点 要么值为 2 要么值为 3 ,其中 2 表示逻辑或 OR3 表示逻辑与 AND

计算 一个节点的值方式如下:

  • 如果节点是个叶子节点,那么节点的 为它本身,即 True 或者 False
  • 否则,计算 两个孩子的节点值,然后将该节点的运算符对两个孩子值进行 运算

返回根节点 root 的布尔运算值。

完整二叉树 是每个节点有 0 个或者 2 个孩子的二叉树。

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


题解

给一颗完整二叉树,孩子节点要么是0要么是2,不存在1个孩子节点。

叶子节点和非叶子节点数字分别代表不同的意思,最后将root根结果bool值返回去。

解法:递归

宏观角度看待递归

当想知道root根节点bool值时

  • 我要先知道左子树bool值,在知道右子树bool值,然后将左右子数的bool值和root本身信息做整合。
  • 当我们要知道左子树和右子树的bool值,你会发现它和大问题是一样的。
  • 大问题是解决整棵树的bool值,小问题是解决左子树bool值,右子树bool值。
  • 此时把左指针传给dfs,dfs把左子树bool值给你,把右指针传给dfs,dfs把右子树bool值给你。

所以dfs非常好设计。

1. 函数头

bool dfs(TreeNode* root)

2. 函数体

先求左子树bool值,在求右子树bool,不要管递归如何执行,只需要知道你给它数据它一定能完成任务。

  • bool left=dfs(root->left)
  • bool right=dfs(root->right)

最后在将左子树和右子树bool值和root做整合。

3. 递归出口

到叶子节点就不需要往后递归了,此时返回结果就行了

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool evaluateTree(TreeNode* root) 
    {
        return dfs(root);
    }

    bool dfs(TreeNode* root)
    {
         if(root->left == nullptr)
    {
        return root->val;
    }
        
        bool left=dfs(root->left);
        bool right=dfs(root->right);

        return root->val == 2 ? left | right : left & right;
//叶子节点 才有值  其他都是操作符
    }
};

2.求根节点到叶节点数字之和

链接:129. 求根节点到叶节点数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 09 之间的数字。

每条从根节点到叶节点的路径都代表一个数字:

  • 例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123

计算从根节点到叶节点生成的 所有数字之和

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


将根到叶子节点组合的数加起来

解法:递归

首先我们要找到 相同子问题,在树中相同子问题很好找,只需要关注递归到每一层需要干什么事情即可。

当它们干的都是一样的事情,这就是相同的子问题。

当递归到5这一层,我发现要拿到和5相连下面的叶子节点的数把它们加起来,然后返回给根节点。当到5这一层

  • 首先要把12先给我传过来,不然我怎么算出1258,也就是到某一层要把它之前路径上之后给我传过来 tmp。然后拿到这个12,先算出125。
  • 然后把125传给左边
  • 把125传给右边
  • 然后把左边值的和与右边值的和相加,return 上一层节点 ret

在这5这一层要完成4个任务,同理其他层也是要完成相同的任务。

1.函数头

首先要有一个返回值,就是传给dfs一个根节点把和它相连的叶子节点的和返回

  • 但是要注意的是,我们除了要传一个根节点,还需要再把递归到该层的上面路径和拿到!
  • 此函数头是int dfs(root,num)

2.函数体

1、2、3、4个步骤

3.递归出口

  • 当到叶子节点就可以返回了,但是要注意的是,这个递归出应该在步骤1之后的
  • 因为到叶子节点, 也要把叶子节点的数加上才向上返回的。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int sumNumbers(TreeNode* root) 
    {
        return dfs(root,0);
    }

//存上一层的num
    int dfs(TreeNode* root,int num)
    {
        //更新  当前层数值
        num=num*10+root->val;
    
    //左右为空 就返回这个值
    if(!root->left && !root->right) return num;

        int ret=0;
        if(root->left) ret+=dfs(root->left,num);
        if(root->right) ret+=dfs(root->right,num);

        return ret;

    }
};

3. 二叉树剪枝

链接:814. 二叉树剪枝

给你二叉树的根结点 root ,此外树的每个结点的值要么是 0 ,要么是 1

返回移除了所有不包含 1 的子树的原二叉树。

节点 node 的子树为 node 本身加上所有 node 的后代。


题解

注意这里的剪枝和前面说的剪枝是不一样的,前面的是那条路不是通往终点的路不能在走了,这里的剪枝真的就是把分枝剪掉!

  • 返回移除了所有 不包含 1 的子树 的原二叉树 的意思是,就是如果子树全是0就把它剪掉。
  • 如果子数有1就不能剪掉。

通过决策树,抽象出递归的三个核心问题

也就是说通过,完成二叉树剪枝这个任务,完成递归三个核心问题。

  • 像之前先把每个子问题要完成任务先找到,但是有时候某些道题非常麻烦,无法总结出子问题到底干了什么问题,这道题是给一个根节点然后把子树全为0剪掉然后把新根节点返回来。
  • 但是有些题特别抽象你根本无法总结出来你让这个递归去完成什么任务,任务太多了。此时我们要有一个能力,通过研究递归的过程来总结出函数头、函数体、递归出口!

首先这肯定是一个后序遍历,先要确定左右子树是什么情况才能决定是否把这个子树干掉。

  • 当作后序遍历到底最左节点,然后再后序发现它左为空右为空,然后自己本身也是0,说明可以给它剪枝。
  • 然后回到上一层但是有一个问题,是不是要修改上一层左子树的指针啊,因此这个递归函数要有一个返回值 TreeeNode*。

然后如果左右子树都为空,但自己是1,说明不能剪枝

当我们把根左子树都弄好了,其实整个递归过程就出来。

函数头

  • TreeNode* dfs(root) 给一个根节点,然后把结果给我

函数体

  • 左子树右子树都搞完返回给我一个东西,然后通过返回值 再结合我自己看是否需要把自己删除。
  • 1.左子树、2.右子树、 3.判断

递归出口

  • 遇到null结束,就返回null

递归处理 树

  • 你只需要管它是做什么的,拿去用,而不需要在意它是怎么实现的
  • 我们的pruneTree是用来将树去除不包含1的子树。
  • 那么我们可以用它来干嘛?我们可以对root的左右子树都进行一次pruneTree,现在root的左右子树都是去除了不包含1的子树的子树或者null。
  • 那么我们只需要判断什么?只需要在root不存在左右子树且自身为0时,返回null即可,如果root.val为1或者左右子树存在任一,都需要返回自身。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* pruneTree(TreeNode* root) 
    {
        //出口
        if(!root) return nullptr;

        root->left=pruneTree(root->left);//递归 左子树
        root->right=pruneTree(root->right);//递归 右子树

        //如果 当前是 叶子节点,且值为0,剪枝
        if(!root->left && !root->right && root->val==0)
        return nullptr;

        return root;
        
    }
};

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

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

相关文章

蓝桥杯 之 第27场月赛总结

文章目录 习题1.抓猪拿国一2.蓝桥字符3.蓝桥大使4.拳头对决 习题 比赛地址 1.抓猪拿国一 十分简单的签到题 print(sum(list(range(17))))2.蓝桥字符 常见的字符匹配的问题,是一个二维dp的问题,转化为对应的动态规划求解 力扣的相似题目 可以关注灵神…

可视化动态表单动态表单界的天花板--Formily(阿里开源)

文章目录 1、Formily表单介绍2、安装依赖2.1、安装内核库2.2、 安装 UI 桥接库2.3、Formily 支持多种 UI 组件生态: 3、表单设计器3.1、核心理念3.2、安装3.3、示例源码 4、场景案例-登录注册4.1、Markup Schema 案例4.2、JSON Schema 案例4.3、纯 JSX 案例 1、Form…

Amdahl 定律

Amdahl 定律是用来表示,当提高系统某部分性能时对整个系统的影响,其公式如下: a表示我们提升部分初始耗时比例,k是我们的提升倍率,通过这个公式我们可以轻松的得知对每一部分的提醒,对整个系统带来的影响…

Linux系统之美:环境变量的概念以及基本操作

本节重点 理解环境变量的基本概念学会在指令和代码操作上查询更改环境变量环境变量表的基本概念父子进程间环境变量的继承与隔离 一、引入 1.1 自定义命令(我们的exe) 我们以往的Linux编程经验告诉我们,我们在对一段代码编译形成可执行文件后…

pnpm 报错 Error: Cannot find matching keyid 解决

1. 查看corepack版本,升级至0.31.0 npm i -g corepack0.31.0 这里注意环境变量,可能升级后还是指向旧版本,可以选择更新环境变量或者删除原指向的corepack命令 2. 更新pnpm corepack install -g pnpmlatest 问题解决。

Ubuntu实时读取音乐软件的音频流

文章目录 一. 前言二. 开发环境三. 具体操作四. 实际效果 一. 前言 起因是这样的,我需要在Ubuntu中,实时读取正在播放音乐的音频流,然后对音频进行相关的处理。本来打算使用的PipewireHelvum的方式实现,好处是可以直接利用Helvum…

Fiddler抓包工具最快入门

目录 前言 了解HTTP网络知识 简单了解网络访问过程 简单了解HTTP网络传输协议 工作过程 HTTP请求: Fildder工具使用教程 抓包的概念 一、什么是抓包 二、为什么要抓包 三、抓包的原理(图解) Fiddler工具 安装 使用 Fiddler查看…

编译器与中间表示:LLVM与GCC、G++、Clang的关系详解

编译器与中间表示:LLVM与GCC、G、Clang的关系详解 引言 编译器是软件开发中不可或缺的工具,它负责将高级语言(如C/C、Java等)转换为机器语言,使计算机能够理解和执行程序。中间表示(Intermediate Represe…

股指期货贴水波动,影响哪些投资策略?

先来说说“贴水”。简单来说,贴水就是股指期货的价格比现货价格低。比如,沪深300指数现在是4000点,但股指期货合约的价格只有3950点,这就叫贴水。贴水的大小会影响很多投资策略的收益,接下来我们就来看看具体的影响。 …

RHCE 使用nginx搭建网站

一。准备工作 Windows dns映射 创建目录网页 vim 编辑内容 添加如下 重启nginx服务,在Windows浏览器进行测试

AtCoder Beginner Contest 398(ABCDEF)

A - Doors in the Center 翻译: 找到一个满足下面情况长为N的字符串: 每个字符是 - 或 。是一个回文。包含一个或两个 。如果包含两个相邻的 。 如此字符串为独一无二的。 思路: 从两端使用 开始构造回文。在特判下中间部分,…

单表达式倒计时工具:datetime的极度优雅(智普清言)

一个简单表达式,也可以优雅自成工具。 笔记模板由python脚本于2025-03-22 20:25:49创建,本篇笔记适合任意喜欢学习的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值:在于输出思考与经验,而不仅仅是知识的简单复述。 Pyth…

C++继承机制:从基础到避坑详细解说

目录 1.继承的概念及定义 1.1继承的概念 1.2 继承定义 1.2.1定义格式 1.2.2继承关系和访问限定符 1.2.3继承基类成员访问方式的变化 总结: 2.基类和派生类对象赋值转换 3.继承中的作用域 4.派生类的默认成员函数 ​编辑 默认构造与传参构造 拷贝构造&am…

MySQL数据库精研之旅第二期:库操作的深度探索

专栏:MySQL数据库成长记 个人主页:手握风云 目录 一、查看数据库 二、创建数据库 2.1. 语法 2.2. 示例 三、字符集编码和校验(排序)规则 3.1. 查看数据库支持的字符集编码 3.2. 查看数据库支持的排序规则 3.3. 不同的字串集与排序规则对数据库的…

git_version_control_proper_practice

git_version_control_proper_practice version control,版本控制的方法之一就是打tag 因为多人协作的项目团队,commit很多,所以需要给重要的commit打tag,方便checkout,检出这个tag 参考行业的实践方式。如图git、linux…

计算机组成原理和计算机网络常见单位分类及换算

计算机组成原理(主要用于存储、内存、缓存等) 计算机网络(主要用于传输速率) 直观对比

【第二十八周】:Temporal Segment Networks:用于视频动作识别的时间分段网络

TSN 摘要Abstract文章信息引言方法时间分段采样分段聚合输入模态聚合函数多尺度时序窗口集成(M-TWI)训练 代码实现实验结果总结 摘要 本篇博客介绍了时间分段网络(Temporal Segment Network, TSN),这是一种针对视频动…

扩展域并查集

什么叫扩展域并查集 1 和 2是敌人,那么就把1好12链接起来:表示1和2是敌人 2和11链接起来也是这个道理 然后2 和3使敌人同理。 最后12连接了1 和 3,表名1 和 3 是 2 的敌人,1和3 就是朋友 1.P1892 [BalticOI 2003] 团伙 - 洛谷 #in…

【C#语言】C#同步与异步编程深度解析:让程序学会“一心多用“

文章目录 ⭐前言⭐一、同步编程:单线程的线性世界🌟1、寻找合适的对象✨1) 🌟7、设计应支持变化 ⭐二、异步编程:多任务的协奏曲⭐三、async/await工作原理揭秘⭐四、最佳实践与性能陷阱⭐五、异步编程适用场景⭐六、性能对比实测…

动态规划入门详解

动态规划(Dynamic Programming,简称DP)是一种算法思想,它将问题分解为更小的子问题,然后将子问题的解存起来,避免重复计算。 所以动态规划中每一个状态都是由上一个状态推导出来的,这一点就区别…