当前位置: 首页 > news >正文

专题二 二叉树中的深度优先搜索

例题1.计算布尔二叉树的值

1.题目

题目链接:计算布尔二叉树的值
给你一棵 完整二叉树 的根,这棵树有以下特征:

叶子节点 要么值为 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 。

提示:
树中节点数目在 [1, 1000] 之间。
0 <= Node.val <= 3
每个节点的孩子数为 02 。
叶子节点的值为 01 。
非叶子节点的值为 23

2.算法原理

1.重复子问题——>函数头设计

bool dfs(TreeNode* root)

2.只关心一个子问题在做什么——>函数体的设计

bool left = dfs(root->left);
bool right = dfs(root->right);
return left||right or left&&right

3.递归的退出

root->val == 0  return false;
root->val == 1  return true;

3.编写代码

class Solution {
public:bool evaluateTree(TreeNode* root) {if(root->left == nullptr) return root->val == 0? false : true;bool left = evaluateTree(root->left);bool right = evaluateTree(root->right);return root->val == 2? left|right : left&right;}
};

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

1.题目

题目链接:求根节点到叶节点数字之和
给你一个二叉树的根节点 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, 1000]0 <= Node.val <= 9
树的深度不超过 10

2.算法原理

1.重复子问题——>函数头设计
int dfs(root, presum)
2.只关心一个子问题在做什么——>函数体的设计
在这里插入图片描述

函数体步骤如上图中1,2,3,4,5,6
3.递归的退出
遇到叶子节点时返回,注意这一步要插入在1步骤后面,因为确保到达叶子节点后,要把叶子节点的值算上,所以先执行1后进行递归退出的判断。

3.编写代码

class Solution {
public:int sumNumbers(TreeNode* root) {return dfs(root, 0);}int dfs(TreeNode* root, int presum){presum = presum*10 + root->val;//①if(root->left == nullptr && root->right == nullptr)//递归出口{return presum;}int ret = 0;if(root->left != nullptr) ret += dfs(root->left,presum);//②③if(root->right!= nullptr) ret += dfs(root->right,presum);//④⑤return ret;//⑥}
};

例题3.二叉树剪枝

1.题目

题目链接:二叉树剪枝
给你二叉树的根结点 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]

提示:
树中节点的数目在范围 [1, 200] 内
Node.val 为 01

2.算法原理

通过决策树,抽象出递归的三个核心问题。
后序遍历
1.重复子问题——>函数头设计

TreeNode* dfs(TreeNode* root)

2.只关心一个子问题在做什么——>函数体的设计
1.处理左子树 root->left = pruneTree(root->left);
2.处理右子树 root->right = pruneTree(root->right);
3.判断

if(root->val == 0 && root->left == nullptr && root->right == nullptr)
{return nullptr;
}

3.递归的退出

root == nullptr 

3.编写代码

class Solution {
public:TreeNode* pruneTree(TreeNode* root) {if(root == nullptr){return nullptr;}root->left = pruneTree(root->left);root->right = pruneTree(root->right);if(root->val == 0 && root->left == nullptr && root->right == nullptr){return nullptr;}return root;}
};

例题4.验证二叉搜索树

1.题目

题目链接:验证二叉搜索树
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

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

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

示例 1:
在这里插入图片描述

输入:root = [2,1,3] 输出:true
示例 2:
在这里插入图片描述
输入:root = [5,1,4,null,null,3,6] 输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。

提示:
树中节点数目范围在[1, 104]-231 <= Node.val <= 231 - 1

2.算法原理

性质:二叉搜索树的中序遍历的结果是一个有序的序列。

策略1:左子树是二叉搜索树,当前节点也符合二叉搜索树定义,右子树也是二叉搜索树。
在这里插入图片描述
以这个图为例,只考虑左边的分支,left,right,cur是三个bool类型的数值,分别用来判定左子树是否是二叉搜索树,右子树是否是二叉搜索树,根节点是否满足二叉搜索树。若都满足,则返回三者的&,作为判断整个子树是否为二叉搜索树。

策略2:策略1的优化,加入剪枝,当找到不符合搜索二叉树的节点时,直接向上返回false即可,无需进行其他多余的遍历。

由于中序遍历顺序是,左→中→右,如果左子树不满足搜索二叉树,直接进行返回false,即左→判断→中→右,同理,若中间节点与左子树关系不满足搜索二叉树,直接进行返回false,即左→判断→中→判断→右

1.设置全局变量的优势
设置全局变量prev,让其对比每个节点的数值,这样就不用在每次递归中传输变量记录节点的值了。

2.关于回溯
在这里插入图片描述

3.关于剪枝
作用:加快搜索过程。
在这里插入图片描述

3.编写代码

策略1:左子树是二叉搜索树,当前节点也符合二叉搜索树定义,右子树也是二叉搜索树。

class Solution {
public:long prev = LONG_MIN;bool isValidBST(TreeNode* root) {if(root == nullptr){return true;}bool left = isValidBST(root->left);//左子树bool cur = false;//根节点if(root->val > prev){cur = true;}prev = root->val;bool right = isValidBST(root->right);//右节点return right && left && cur; }
};

策略2:策略1的优化,加入剪枝,当找到不符合搜索二叉树的节点时,直接向上返回false即可,无需进行其他多余的遍历。

class Solution {
public:long prev = LONG_MIN;bool isValidBST(TreeNode* root) {if(root == nullptr){return true;}bool left = isValidBST(root->left);//左子树if(left == false) return false;//剪枝bool cur = false;//根节点if(root->val > prev){cur = true;}//else//或者//{//return false;//剪枝//}if(left == false) return false;//剪枝prev = root->val;bool right = isValidBST(root->right);//右节点return right && left && cur; }
};

例题5.二叉树中第k小的元素

1.题目

题目链接:二叉树中第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

提示:树中的节点数为 n 。
1 <= k <= n <= 104
0 <= Node.val <= 104

进阶:如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?

2.算法原理

1.两个全局变量 count,ret + 中序遍历

  • 通过中序遍历二叉搜索树(天然升序的性质
  • 每访问一个节点就将 count 减 1
  • 当 count 减到 0 时,当前节点就是第 k 小的元素,将该元素保存在ret中
    在这里插入图片描述
    2.剪枝优化
    左→判断→中→判断→右
  1. 在访问当前节点之前(左子树遍历后),先检查count是否已经为 0,如果是则直接返回,不再处理当前节点和右子树。
  2. 当找到目标元素(count减到 0 时),立即返回,不再遍历右子树。

3.编写代码

class Solution {
public:int count;int ret;int kthSmallest(TreeNode* root, int k) {count = k;dfs(root);return ret;}void dfs(TreeNode* root){if(root == nullptr)return;dfs(root->left);count--;if(count == 0)ret = root->val;dfs(root->right);}
};

加入剪枝优化

class Solution {
public:int count;int ret;int kthSmallest(TreeNode* root, int k) {count = k;dfs(root);return ret;}void dfs(TreeNode* root){if(root == nullptr)return;dfs(root->left);if(count == 0) return;//剪枝优化count--;if(count == 0){ret = root->val;return;//剪枝优化}dfs(root->right);}
};

例题6.二叉树的所有路径

1.题目

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

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

示例 1:
在这里插入图片描述
输入:root = [1,2,3,null,5] 输出:[“1->2->5”,“1->3”] 示例 2: 输入:root = [1]
输出:[“1”]

提示:
树中节点的数目在范围 [1, 100] 内
-100 <= Node.val <= 100

2.算法原理

1.全局变量

vector<string> ret;

2.回溯→恢复现场
函数头

void dfs(root,path)//这里path变量传入的作用是“恢复现场”

函数体

  • 是叶子节点
    将root->val追加在path上,注意此时是找到了一条完整的路径,ret.push_back()将完整路径保存。
  • 不是叶子节点
    将root->val + “->”追加在path上.

☆关于恢复现场

string path变量作全局变量——>只能手动恢复现场
在这里插入图片描述
string path作函数参数,作为临时变量——>自动恢复现场
在这里插入图片描述

递归出口

root == nullptr  return

3.剪枝
无所谓,反正都要遍历!
一定要做的话,不进入空节点,检测到左或右子树为空的话,干脆不进入!

3.编写代码

class Solution {
public:vector<string> ret;vector<string> binaryTreePaths(TreeNode* root) {string path = "";dfs(root, path);return ret;}void dfs(TreeNode* root, string path)//不能带引用,否则无法实现自动“恢复现场”{if(root == nullptr)return;if(root->right == nullptr &&root->left == nullptr)//找到叶子节点,即找到一条完整路径{path += to_string(root->val);ret.push_back(path);}else{path += to_string(root->val);path += "->";}dfs(root->left,path);dfs(root->right,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)//不能带引用,否则无法实现自动“恢复现场”{if(root->right == nullptr &&root->left == nullptr)//找到叶子节点,即找到一条完整路径{path += to_string(root->val);ret.push_back(path);return;//剪枝,对于叶子节点,不用再看左右子树了,一定都是nullptr}else{path += to_string(root->val);path += "->";}if(root->left) dfs(root->left,path);//剪枝,左子树为空的话,直接不进入if(root->right) dfs(root->right,path);}};
http://www.dtcms.com/a/390004.html

相关文章:

  • Git 多人协作(1)
  • 设计模式第三章(迭代器模式)
  • 网络原理(4):HTTP协议 -- HTTP请求 -- 首行(请求方法)
  • 密钥下发服务中心:双重验证 + 实时监控的轻量级密钥管理解决方案
  • 硬件 - RK3588部分(4) - 原理图 - RK806
  • Sass开发【三】
  • 百度之星2025(第二场)
  • Ovis-U1:阿里巴巴推出的统一的多模态理解与生成模型
  • 深入剖析C++智能指针:unique_ptr与shared_ptr的资源管理哲学
  • 创建索引失败,表一直查询不了
  • 知识分享:网线和DB9正确接线方法
  • 【算法笔记】前缀树
  • 让ai完成原神调酒 试做
  • 第十四届蓝桥杯青少组C++选拔赛[2022.11.27]第二部分编程题(2、拼写单词)
  • 私有化部署UE像素流后,通过实时云渲染平台配置网络端口,实现云推流内网及公网访问
  • Day 05 Geant4多线程 Multithreading --------以B1为例
  • 【word解析】从 Word 提取数学公式并渲染到 Web 页面的完整指南
  • FreeRTOS 队列机制详解:阻塞、唤醒与任务同步
  • Unity学习之UI优化总结
  • 基于微信小程序蓝牙信标 (Beacon)的室内导航实例
  • 用Comate Zulu开发一款微信小程序
  • 触觉智能Purple Pi OH2开发板配置参数
  • 鸿蒙Next应用文件管理全攻略:从基础操作到高级实践
  • 云手机对《黑神话:悟空》的作用都有哪些?
  • Leetcode 994. 腐烂的橘子 多源 BFS
  • 微硕WSP4982双N沟MOSFET,赋能汽车智能座椅通风系统
  • BMP280 气压计驱动
  • 速通ACM省铜第八天 赋源码(1709)
  • InnoDB索引结构与排序构建机制详解
  • mmpose可视化出错,图像与关键点对不上