算法:[递归/搜索/回溯]二叉树的深搜

目录

题目一:计算布尔二叉树的值

题目二:求根节点到叶节点数字之和

题目三:二叉树剪枝

题目四:验证二叉搜索树

题目五:二叉搜索树中第k小的元素

题目六:二叉树的所有路径


题目一:计算布尔二叉树的值

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

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

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

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

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

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

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

示例 1:

输入:root = [2,1,3,null,null,0,1]
输出:true
解释:上图展示了计算过程。
AND 与运算节点的值为 False AND True = False 。
OR 运算节点的值为 True OR False = True 。
根节点的值为 True ,所以我们返回 true 。

示例 2:

输入:root = [0]
输出:false
解释:根节点是叶子节点,且值为 false,所以我们返回 false 。

既然是二叉树相关的题目,就考虑使用递归来解决,dfs

题目告诉我们是完整二叉树,也就是每个结点如果有孩子,那就必然有两个孩子,并且叶子结点是true或false,非叶子结点是 | 或 &,通过非叶子的运算符,计算两个叶子结点的值

所以可以宏观看待:

想要得到整棵树的结果,就需要知道根节点左子树和右子树的结果,知道结果后,就可以根据根节点的运算符来计算最终的结果

而子问题同样也是这样,想知道左子树的结果,同样进行上述操作

1、重复子问题(函数头的设计)

bool dfs(root)

2、只关心某个子问题在做什么(函数体的设计)

bool left = dfs(root->left);
bool right= dfs(root->right);
计算最终结果

3、递归的出口(边界情况)

到叶子结点时就不需要计算结果了,因为叶子结点存储的本身就是 true 或 false

代码如下:

class Solution 
{
public:bool evaluateTree(TreeNode* root) {// 叶子结点时就return,不用判断右孩子,因为左右孩子的状态一样if(root->left == nullptr) return root->val == 0 ? false : true;// 计算左右子树的结果bool left = evaluateTree(root->left);bool right = evaluateTree(root->right);// 通过root的值和左右子树的结果做计算return root->val == 2 ? left | right : left & right;}
};

题目二:求根节点到叶节点数字之和

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

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

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

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

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

示例 1:

输入:root = [1,2,3]
输出:25
解释:
从根到叶子节点路径 1->2 代表数字 12
从根到叶子节点路径 1->3 代表数字 13
因此,数字总和 = 12 + 13 = 25

示例 2:

输入:root = [4,9,0,5,1]
输出:1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495
从根到叶子节点路径 4->9->1 代表数字 491
从根到叶子节点路径 4->0 代表数字 40
因此,数字总和 = 495 + 491 + 40 = 1026

这道题同样使用递归解决,那么递归就需要找到相同的子问题,依旧是下面几步得到解法:

1、重复子问题(函数头的设计)

因为每一个结点都需要返回,到当前结点时代表的数字是多少,所以需要有一个int的返回值,而函数参数不光需要有结点的指针,还需要有之前节点算出来的数字,所以函数头应该为:

int dfs(Node* root, int prevnum);

2、只关心某个子问题在做什么(函数体的设计)

需要找到相同的子问题:

①计算出之前的数字结合当前结点的数字后,最终的数字是多少
②将该结点最后得到的数字给左孩子
③将该结点最后得到的数字给右孩子
④将该结点的孩子节点返回来的值拿到,并相加返回给上一层

3、递归的出口(边界情况)

递归的出口就是到叶子结点时,就退出

不同的是:这里到了叶子结点,也需要先将这个结点的数字与之前的数字结合,再退出

代码如下:

class Solution 
{
public:// prevnum表示前驱和int dfs(TreeNode* root, int prevnum){// 第一步,先将prevnum与前面得到的 前驱和 组合prevnum = prevnum * 10 + root->val;// 到叶子结点返回即可,递归出口if(root->left == nullptr && root->right == nullptr)return prevnum;int ret = 0;// 第二、三步,进入当前结点的左右字树中if(root->left) ret += dfs(root->left, prevnum);if(root->right) ret += dfs(root->right, prevnum);// 第四步,返回当前节点左右字树构成数字的和return ret;}int sumNumbers(TreeNode* root) {// 初始的前驱和为0return dfs(root, 0);}
};

题目三:二叉树剪枝

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

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

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

示例 1:

输入:root = [1,null,0,0,1]
输出:[1,null,0,null,1]
解释:
只有红色节点满足条件“所有不包含 1 的子树”。 右图为返回的答案。

示例 2:

输入:root = [1,0,1,0,0,0,1]
输出:[1,null,1,null,1]

示例 3:

输入:root = [1,1,0,1,1,0,1,0]
输出:[1,1,0,1,1,null,1]

这里的剪枝,指的是子树中全部是0时,才会剪枝

又因为判断该节点是否剪枝,需要看该节点的左右孩子的值是否全部为0,所以是后序遍历的过程

1、重复子问题(函数头的设计)

因为每次子树剪枝或不剪枝,都需要返回给上一层,所以需要返回值,且只需要传入节点指针即可

Node* dfs(Node* root)

2、只关心某个子问题在做什么(函数体的设计)

只研究一个结点,得到子问题在做的事情

①处理当前节点的左子树
②处理当前结点的右子树
③根据左右字树的结果,以及自身的值,判断是否剪枝

3、递归的出口(边界情况)

边界情况也比较简单,当运行到nullptr时,就return

root == nullptr

代码如下:

class Solution 
{
public:TreeNode* pruneTree(TreeNode* root) {// 边界条件if(root == nullptr) return nullptr;// 判断左右字树是否为空,以及自身的val是否为0root->left = pruneTree(root->left);root->right = pruneTree(root->right);if(root->left == nullptr && root->right == nullptr && root->val == 0)root = nullptr;// 返回给上一层return root;}
};

题目四:验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左

    子树

    只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

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

示例 2:

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

二叉搜索树也就是你的左子树必须要全部小于根节点,右子树必须全部大于根节点,并且左右字树也是符合上述的要求,此时这棵树就称之为二叉搜索树

我们知道二叉搜索树中序遍历的结果,是一个有序的序列

非常容易想到的一种方式就是将这个树中序遍历一遍,再将遍历后的结果存入一个数组中,再判断该数组是否是有序的,这种方式可行,但是消耗空间太多了,空间复杂度是O(N)了

那么换一种思路,既然中序遍历是有序的,那么我每次遍历到一个节点时,可以肯定的是:该结点是比上一个结点大的,所以就根据这个思路,不需要将整个树中序遍历一遍再判断遍历的结果,而是每次遍历到一个结点时就判断是否大于上一个结点的val

所以这里只需要创建一个全局变量prev,这样就不需要dfs的时候还需要传入一个变量prev了

当发现一个结点不符合上述规定时,就直接返回false,不需要再中序遍历其他没有遍历到的子树了

代码如下:

class Solution 
{
public:// 因为题目中最小的就是INT_MIN,所以这里使用long类型的最小值long prev = LONG_MIN;bool isValidBST(TreeNode* root) {// 根节点是nullptr,就返回trueif(root == nullptr) return true;// 先拿到左子树的结果bool left = isValidBST(root->left);// 剪枝,如果一个节点是false,就不需要遍历其他的了  if(left == false) return false;// 判断当前节点if(root->val > prev) prev = root->val;else return false; // 同样是剪枝操作// 再拿到右子树的结果bool right = isValidBST(root->right);return left && right;}
};

题目五:二叉搜索树中第k小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。

示例 1:

输入:root = [3,1,4,null,2], k = 1
输出:1

示例 2:

输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3

这道题其实在我们知道中序遍历的结果是有序的这个概念后,就非常简单了,也就是中序遍历的过程中,遍历一次计数一次,直到第k次就是题目所求的元素

同样,这里遍历时可以使用全局遍历,这样就不需要都传参数了

几乎每一个递归题都会体现回溯的特性,就按中序遍历的题看,每当走到空节点时,就不会继续往下递归了,而会返回上一级的结点,这就叫做回溯

也就是设定两个全局遍历,一个用来计数,一个用来当找到第k小个元素时记录该元素,并且在代码中也可以加上剪枝的操作,也就是当找到第k小个元素后,就不需要继续遍历了

代码如下:

class Solution 
{
public:int count;   // count是用来计数的int ret = 0; // ret是最终结果int kthSmallest(TreeNode* root, int k) {count = k;dfs(root);return ret;}void dfs(TreeNode* root){// if中加上count == 0,进行剪枝操作if(root == nullptr || count == 0) return;dfs(root->left);count--;if(count == 0) {ret = root->val;return;}dfs(root->right);}
};

题目六:二叉树的所有路径

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

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

示例 1:

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

示例 2:

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

这道题比较简单,为了说明 回溯 -> 恢复现场 这个情况

前面说到了只要有深度优先dfs,必然会出现回溯,而只要回溯,就必然会有恢复现场

所以总结一下:只要有深度优先变量,就必然会有恢复现场的操作

下面说说这道题的思路,其实很简单,就是从根节点开始,每次都记录路径,到叶子节点后就存储当前的路径,直到循环完这棵树为止

这时解题中,会定义一个全局变量ret,ret是string类型的数组用于存储所有的路径

而string类型的path存储每次的路径,在该题中不适合设置为全局变量,因为每次回溯时都需要pop,甚至需要pop好几个字符,比较麻烦,所以该题中将path当做参数传入是比较方便的

如果没有将path设置为局部变量,而是设置为了全局变量,这时就会引出恢复现场这个动作,因为当执行到一个叶子结点处,将叶子结点的路径path存入ret后,path会回溯到上一层,这时如果不恢复现场,path中就会多一个叶子结点,不符合题意,所以需要恢复现场

代码如下:

class Solution 
{
public:vector<string> ret; // 最终结果vector<string> binaryTreePaths(TreeNode* root) {string path;dfs(root, path);return ret;}void dfs(TreeNode* root, string path){path += to_string(root->val);// 这里直接剪枝,如果判断出是叶子结点,直接return,不需要进入if(root->left == nullptr && root->right == nullptr){ret.push_back(path);return;}path += "->";// 这里的if判断其实就是剪枝的操作,如果为空就不进入了if(root->left) dfs(root->left, path);if(root->right) dfs(root->right, path);}
};

二叉树的深搜题目到此结束

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

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

相关文章

【C语言】宏定义常量加 ; 的错误

我在使用宏定义常量定义二维数组的时候&#xff0c;编译器报错&#xff1a;应输入“]”&#xff0c;如下&#xff1a; 原因是宏定义不是C语言规定的语句&#xff0c;它的结尾不加 ; 。在上图的 int mine[EASY_ROWS][EASY_COLS]; 中&#xff0c;把 EASY_ROWS 替换为了 9;2; &…

【秋招笔试题】小明的美食

解析&#xff1a;思维题。由于需要互不相同&#xff0c;每次操作取重复的值与最大值相加即可&#xff0c;这样即可保证相加后不会新增重复的值。因此统计重复值即可。 #include <iostream> #include <algorithm>using namespace std; const int maxn 1e5 5; int…

大模型算法面试题(十一)

本系列收纳各种大模型面试题及答案。 1、说一下目前主流或前沿的预训练模型&#xff0c;包括nlp&#xff08;百度ERNIE3.0&#xff0c;华为NEZHA&#xff0c;openAI gpt-3&#xff0c;nvidia MegatronLM&#xff0c;macrosoft T5&#xff09;和cv&#xff08;我只知道CLIP&…

wordpress主题Typecho仿百度响应式主题Xaink

wordpress主题Typecho仿百度响应式主题Xaink 新闻类型博客主题&#xff0c;简洁好看&#xff0c;适合资讯类、快讯类、新闻类博客建站&#xff0c;响应式设计&#xff0c;支持明亮和黑暗模式 直接下载 zip 源码->解压后移动到 Typecho 主题目录->改名为xaink->启用

内衣洗衣机和手洗哪个干净?推荐五款品质优良精品

在日常生活中&#xff0c;内衣洗衣机已成为现代家庭必备的重要家电之一。选择一款耐用、质量优秀的内衣洗衣机&#xff0c;不仅可以减少洗衣负担&#xff0c;还能提供高效的洗涤效果。然而&#xff0c;市场上众多内衣洗衣机品牌琳琅满目&#xff0c;让我们往往难以选择。那么&a…

实时捕获数据库变更

1.CDC概述 CDC 的全称是 Change Data Capture &#xff0c;在广义的概念上&#xff0c;只要能捕获数据变更的技术&#xff0c;我们都可以称为 CDC 。我们目前通常描述的CDC 技术主要面向数据库的变更&#xff0c;是一种用于捕获数据库中数据变更的技术&#xff0c;CDC 技术应用…

Linux嵌入式学习——数据结构——队列

一、概念 1&#xff09;定义 是只允许在一端进行插入操作&#xff0c;而在另一端进行删除操作的线性表 队列 是一种 先进先出&#xff08;First In First Out&#xff09; 的线性表 线性表有顺序存储和链式存储&#xff0c;栈是线性表&#xff0c;所以有这两种存储方式 同样…

【在开发小程序的时候如何排查问题】

在开发小程序的时候如何排查问题 在最近开发小程序的时候&#xff0c;经常出现本地在浏览器中调试没有问题&#xff0c;但是一发布到预发环境就出现各种个样的问题 手机兼用性问题 有时候会出现苹果&#x1f34e;手机键盘弹出&#xff0c;导致ui界面高度出现异常边界问题&#…

Arduino IDE界面和设置(基础知识)

Arduino IDE界面和设置&#xff08;基础知识&#xff09; 1-2 Arduino IDE界面和设置如何来正确选择Arduino开发板型号如何正确选择Arduino这个端口如何来保存一个Arduino程序Arduino ide 的界面功能按钮验证编译上传新建打开保存工作状态 1-2 Arduino IDE界面和设置 大家好这…

如何穿透模糊,还原图片真实面貌

目录 图像清晰化的魔法棒&#xff1a;AI如何穿透模糊&#xff0c;还原图片真实面貌 前言 论文背景 论文思路 模型介绍 复现过程 演示视频 使用方式 本文所涉及所有资源均在传知代码平台可获取。 图像清晰化的魔法棒&#xff1a;AI如何穿透模糊&#xff0c;还原图片真实面貌 在我…

全网最最实用--模型高效推理:量化基础

文章目录 一、量化基础--计算机中数的表示1. 原码&#xff08;Sign-Magnitude&#xff09;2. 反码&#xff08;Ones Complement&#xff09;3. 补码&#xff08;Twos Complement&#xff09;4. 浮点数&#xff08;Floating Point&#xff09;a.常用的浮点数标准--IEEE 754(FP32…

状态机 XState 使用

状态机 一般指的是有限状态机&#xff08;Finite State Machine&#xff0c;FSM&#xff09;&#xff0c;又可以称为有限状态自动机&#xff08;Finite State Automation&#xff0c;FSA&#xff09;&#xff0c;简称状态机&#xff0c;它是一个数学模型&#xff0c;表示有限个…

【计算机网络】数据链路层实验

一&#xff1a;实验目的 1&#xff1a;学习WireShark软件的抓包操作&#xff0c;分析捕获的以太网的MAC帧结构。 2&#xff1a;学习网络中交换机互相连接、交换机连接计算机的拓扑结构&#xff0c;理解虚拟局域网&#xff08;WLAN&#xff09;的通信机制。 3&#xff1a;学习…

cas 和 synchronized 优化过程

cas 什么是CAS CAS:全称Compareandswap&#xff0c;字⾯意思:”⽐较并交换“&#xff0c;⼀个CAS涉及到以下操作&#xff1a; 我们假设内存中的原数据V&#xff0c;旧的预期值A&#xff0c;需要修改的新值B。 1. ⽐较A与V是否相等。&#xff08;⽐较&#xff09; 2. 如果⽐较…

ubuntu22.04单个网口两个IP

其中 4网段IP可用来上网&#xff0c;3 网段用来内网 界面显示: 配置文件&#xff1a; 01-network-manager-all.yaml 放在 /etc/netplan/ # Let NetworkManager manage all devices on this systemnetwork:version: 2renderer: networkdethernets:eth0:dhcp4: falsedhcp6: …

开放式耳机哪个牌子好?五大超值机型整理,速速收藏!!

大家都知道现在的开放式耳机是越来越火了&#xff0c;后台也有非常多的小伙伴来私信&#xff0c;作为一个耳机测评师&#xff0c;当然是为了你们服务啦&#xff0c;所以这一期文章&#xff0c;就是为了个大家答疑解惑&#xff0c;告诉大家如何才能选购出一款比较好用的开放式耳…

万字长文详解Java反射技术 | JavaSE | Java进阶知识 | 源码

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;今天分享的是JavaSE中的进阶知识&#x1f6d1;&#xff1a;反射技术。内容有点长&#xff0c;非常全面&#xff0c;记得点赞&#x1f44d;、收藏✅加关…

【网络世界】HTTP协议

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 概念 &#x1f4c1; URL &#x1f4c2; urlencode 和 urldecode &#x1f4c1; 协议格式 &#x1f4c1; 方法 &#x1f4c2; GET/get &#x1f4c2; POST/post &#x1f4c1; 常见的报头 &#x1f4c1; 状态码 &…

模型大小的指标和模型量化的指标和手段

一、模型大小的指标 1.计算量 计算次数&#xff0c;反映了模型对硬件计算单元的需求。计算量的单位是 OPs(Operations) 。最常用的数据格式 为 float32&#xff0c;因此float32类型下的计算量单位被写作 FLOPs (Floating Point Operations)&#xff0c;即浮点计算次数。模型的…

3D Web轻量化引擎HOOPS Communicator针对复杂大模型Web端可视化的解决方案

随着工程设计、制造和建筑领域中三维模型的日益复杂化&#xff0c;如何在Web端高效处理和展示这些大规模数据成为一大挑战。HOOPS Communicator作为一款强大的3D可视化工具&#xff0c;提供了一套针对复杂大模型的轻量化解决方案&#xff0c;涵盖了模型轻量化及格式转换、超大模…