深度优先的艺术:探索二叉树的深搜算法精髓

在这里插入图片描述

文章目录

  • 前言
    • ☀️一、计算布尔二叉树的值
      • 🌙解法
      • ⭐代码
    • ☀️二、求根节点到叶节点数字之和
      • 🌙解法
      • ⭐代码
    • ☀️三、二叉树剪枝
      • 🌙解法
      • ⭐代码
    • ☀️四、验证二叉搜索树
      • 🌙解法
      • ☁️步骤
      • ⭐代码
    • ☀️五、二叉搜索树中第k小的元素
      • 🌙解法
      • ☁️步骤
      • ⭐代码
      • 🌀时间和空间复杂度分析
    • ☀️六、二叉树的所有路径
      • 🌙解法
      • ☁️步骤
      • ⭐代码
      • 🌀时间复杂度分析
  • 结语


前言

二叉树作为一种重要的数据结构,在算法领域有着广泛的应用,而深度优先搜索(DFS)是二叉树遍历和操作的核心算法之一。通过 DFS,可以以递归或迭代的方式深入探索树的每一个节点,并高效地解决路径查找、节点计数、最大深度等问题。在这篇文章中,我们将深入剖析二叉树的深搜算法,从基础概念到典型应用,再到代码实现,带你全面掌握这一重要的算法工具。


☀️一、计算布尔二叉树的值

题目链接:https://leetcode.cn/problems/evaluate-boolean-binary-tree/

🌙解法

  1. 相同子问题:左孩子和右孩子进行父节点对应的运算——函数头bool evaluateTree(TreeNode* root)
  2. 对一个子问题进行分析:
    • **先特判:**首先这是一个完整二叉树,如果根结点的左孩子为空,即代表右孩子也为空,则直接返回根节点对应的布尔值
    • **函数体:**我们现在需要的是该结点左右孩子的布尔值,则直接调用evaluateTree(TreeNode* root->left)evaluateTreel(TreeNode* root->right),无条件相信它一定能帮我们得到左右孩子的布尔值
    • 根据父节点的值判断返回表达式的类型

我们结合示例分析:

在这里插入图片描述

⭐代码

/*** 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) {if(!root->left) return root->val == 0? false: true;bool left = evaluateTree(root->left);bool right = evaluateTree(root->right);return root->val == 2? left | right: left & right;}
};

☀️二、求根节点到叶节点数字之和

题目链接:https://leetcode.cn/problems/sum-root-to-leaf-numbers/

🌙解法

如图所示:

  • 我们以5这个点作为示例分析
  • 值得注意的是递归的返回条件是当结点是叶子结点时,需要返回相加后的值,也就是在第一步之后检测

在这里插入图片描述

⭐代码

/*** 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 dfs(TreeNode* root, int prenum){// 1.接收父节点的值并加上自己int nownum = prenum * 10 + root->val;// 2.检测叶子结点,设置递归终止条件if(root->left == nullptr && root->right == nullptr) return nownum;// 3.传值给左右孩子int ret = 0;if(root->left) ret += dfs(root->left, nownum);  if(root->right) ret += dfs(root->right, nownum);// 返回相加后的值return ret; }int sumNumbers(TreeNode* root) {return dfs(root, 0);}
};

☀️三、二叉树剪枝

题目链接:https://leetcode.cn/problems/binary-tree-pruning/

🌙解法

通过决策树分析:

  • 以箭头所指结点分析,我需要左右子树的返回值判断自身的返回值,故此得到函数头——Node* dfs(root)
  • 函数体总共有三点:
    1. 得到左子树的返回值
    2. 得到右子树的返回值
    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:TreeNode* pruneTree(TreeNode* root) {if(root == nullptr) return nullptr;root->left = pruneTree(root->left);root->right = pruneTree(root->right);if(root->left == nullptr && root->right == nullptr && root->val == 0) root = nullptr;return root;   }
};

☀️四、验证二叉搜索树

题目链接:https://leetcode.cn/problems/validate-binary-search-tree/

🌙解法

  • 全局变量的优势

  • 二叉搜索树的中序遍历结果就是一个有序数组

  • 回溯与剪枝,通过剪枝来缩短编译时间

☁️步骤

  1. 左子树是二叉搜索树
  2. 判断当前节点是否满足二叉搜索树的定义
  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 {long prev = LONG_MIN;	// 定义并初始化全局变量prev,用于记录遍历过程中访问过的节点的最大值。
public:bool isValidBST(TreeNode* root) {if(!root) return true;	// 如果当前节点为空(即已经遍历到叶子节点的下一个位置),则返回true。因为在二叉搜索树的定义中,空树被认为是有效的二叉搜索树。bool left = isValidBST(root->left);// 剪枝if(!left) return false;bool cur = false;if(prev < root->val)cur = true;prev = root->val;// 剪枝if(!cur) return false;bool right = isValidBST(root->right);return left && right && cur; // 如果左子树、右子树以及当前节点都满足二叉搜索树的条件,则返回true,表示整个树是有效的二叉搜索树。}
};

☀️五、二叉搜索树中第k小的元素

题目链接:https://leetcode.cn/problems/kth-smallest-element-in-a-bst/description/

🌙解法

  • 利用中序遍历性质
    • 中序遍历的顺序是:左子树 → 根节点 → 右子树。
    • 二叉搜索树的中序遍历会生成一个从小到大的排序序列,因此可以通过遍历的顺序找到第 k 个节点。
  • 递归实现中序遍历
    • 递归函数访问左子树时优先考虑小值。
    • 通过计数器 count 记录需要跳过的节点数,当 count == 0 时,当前节点即为目标值。

☁️步骤

  1. 初始化变量
    • count 用于记录剩余需要遍历的节点数,初始值为 k
    • ret 存储最终找到的第 k 小元素。
  2. 定义递归函数 dfs
    • 如果当前节点为空(root == NULL),直接返回。
    • 优先递归访问左子树:dfs(root->left)
    • 每访问一个节点时,递减计数器:count--
    • 检查是否找到目标元素(count == 0),如果是,将当前节点值存入 ret
    • 然后递归访问右子树:dfs(root->right)
  3. 调用递归函数
    • kthSmallest 方法中,设置 count = k,然后调用 dfs(root) 开始中序遍历。
    • 遍历结束后,ret 中存储的就是第 k 小的元素。
  4. 返回结果
    • 返回 ret,即目标值。

如图分析:

在这里插入图片描述

⭐代码

/*** 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 count;int ret = 0;void dfs(TreeNode* root) {if (!root || count == 0) return;dfs(root->left);count--;if (count == 0)  ret = root->val;dfs(root->right);}int kthSmallest(TreeNode* root, int k) {count = k;dfs(root);return ret;}
};

🌀时间和空间复杂度分析

  1. 时间复杂度
    • 最坏情况下,需要访问所有节点,复杂度为 O ( N ) O(N) O(N)
    • 平均情况下,可以在找到第 k 个元素时提前停止遍历,复杂度接近 O O O(k)。
  2. 空间复杂度
    • 递归调用栈的空间复杂度为树的高度 O ( H ) O(H) O(H)
    • 平衡二叉树的高度 H = l o g N H=logN H=logN;最坏情况下 H = N H=N H=N(退化为链表)。

☀️六、二叉树的所有路径

题目链接:https://leetcode.cn/problems/binary-tree-paths/

🌙解法

  1. 二叉树的性质
    • 每条路径可以通过深度优先搜索(DFS)遍历二叉树来构造。
    • 根节点到叶子节点的路径是每次遍历到叶子节点时的完整路径。
  2. 实现方法
    • 使用递归(DFS)逐层遍历节点,并构造当前路径。
    • 在叶子节点处,将路径加入结果集。

☁️步骤

  1. 定义数据结构与初始化
    • 定义 vector<string> ret 存储所有路径的结果。
    • 定义 string path 存储当前路径的字符串。
  2. 递归的逻辑(DFS)
    • 如果当前节点为空,直接返回。
    • 如果当前节点是叶子节点(左右子树都为空):
      • 将当前节点值拼接到路径字符串中。
      • 把路径字符串加入到结果集 ret 中。
    • 如果当前节点不是叶子节点:
      • 将当前节点值拼接到路径字符串中,并在后面加上 "->" 表示继续延续路径。
      • 递归遍历左子树和右子树,将更新后的路径传递下去。
  3. 返回结果
    • 递归完成后,返回结果数组 ret

如图分析:

在这里插入图片描述

⭐代码

/*** 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:vector<string> ret;vector<string> binaryTreePaths(TreeNode* root) {string path;dfs(root, path);return ret;}void dfs(TreeNode* root, string path){if(!root) return;if(!root->left && !root->right){path += to_string(root->val);ret.push_back(path);return;}else{path += to_string(root->val) + "-" + ">";}dfs(root->left, path);dfs(root->right, path);}
};

🌀时间复杂度分析

  1. 时间复杂度
    • 每个节点被访问一次,复杂度为 O ( N ) O(N) O(N),其中 N N N 是节点总数。
    • 字符串拼接的复杂度为 O ( L ) O(L) O(L),其中 L L L 是路径长度。由于二叉树的深度最大为 H H H ,总复杂度为 O ( N ⋅ H ) O(N⋅H) O(NH)
  2. 空间复杂度
    • 栈的递归深度为树的高度 H H H ,因此空间复杂度为 O ( H ) O(H) O(H)
    • 结果数组 ret 占用的空间为 O ( N ) O(N) O(N)

结语

深度优先搜索不仅是二叉树操作的基础算法,更是一种处理递归结构问题的通用策略。通过对 DFS 的深入理解和实践,可以在许多复杂问题中找到高效的解决方案。从基础到应用,我们希望这篇文章帮助你更好地掌握 DFS 算法,并在未来的编程之路上将其灵活运用到各类数据结构和问题中。记住,算法的艺术在于实践,而实践则在于深度的探索!
在这里插入图片描述

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,17的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是17前进的动力!

在这里插入图片描述

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

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

相关文章

VB.net进行CAD二次开发(二)与cad交互

开发过程遇到了一个问题&#xff1a;自制窗口与控件与CAD的交互。 启动类&#xff0c;调用非模式窗口 Imports Autodesk.AutoCAD.Runtime Public Class Class1 //CAD启动界面 <CommandMethod("US")> Public Sub UiStart() Dim myfrom As Form1 New…

linux ls -l 输出 drwxr-xr-x 2 root root 4096 Dec 5 21:48 rootTest 是什么意思

在Linux系统中&#xff0c;ls -l命令用于以长格式列出目录内容的详细信息。输出的一行通常包含以下部分&#xff1a; drwxr-xr-x 2 root root 4096 Dec 5 21:48 rootTest这一行的各个部分意义如下&#xff1a; drwxr-xr-x&#xff1a;这是文件类型和权限的标识。 第一个字符d…

python学opencv|读取图像(五)读取灰度图像像素

【1】引言 前序学习了图像的基本读取&#xff0c;掌握了imread()、imshow()和imwrite()函数的基本功能和使用技巧&#xff0c;参考文章链接为&#xff1a; python学opencv|读取图像-CSDN博客 然后陆续掌握了彩色图像保存、图像放大和缩小以及对imshow()函数的大胆尝试技巧&a…

基于yolov8的SAR影像目标检测系统,支持图像、视频和摄像实时检测【pytorch框架、python源码】

更多目标检测、图像分类识别、目标追踪等项目可看我主页其他文章 功能演示&#xff1a; 基于yolov8的SAR影像目标检测系统&#xff0c;支持图像、视频和摄像实时检测【pytorch框架、python源码】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov8的SAR影像目标…

2.Flink的项目初始化和Hello-world

目录 1.Flink项目初始化 2.Hello-world 1.Flink项目初始化 新建maven项目或者gradle项目&#xff0c;这里使用maven项目为例。 在项目的pom.xml文件中添加Flink依赖&#xff0c;如下所示&#xff0c;为Hello-World例子的最小依赖&#xff1a; <properties><maven.c…

ESP32开发 云调试

https://blog.csdn.net/weixin_43794311/article/details/128722001 VScode支持的仿真平台 https://docs.wokwi.com/zh-CN/vscode/getting-started 编译&#xff1a;Ctrl Alt B上传并重启模拟器&#xff1a;CtrlShifB Wokwi:Start Simulator调试&#xff1a;CtrlShifB Wokwi…

如何写出一篇好的论文?

写出一篇好的论文需要综合多方面的要素&#xff0c;从选题到最终成文&#xff0c;每一步都至关重要。首先&#xff0c;明确研究主题和目标&#xff0c;确保选题具有创新性、可行性和实际意义。接着&#xff0c;进行深入的文献检索和综述&#xff0c;了解当前领域的研究现状和前…

模版方法模式的理解和实践

在软件开发中&#xff0c;设计模式为我们提供了一套经过验证的解决方案&#xff0c;用于解决常见的设计问题。其中&#xff0c;模版方法模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个算法的框架&#xff0c;并允许子类在不改…

洛谷【排序】算法的题单 - 笔记

2024-12-09 - 第 37 篇 洛谷【排序】题单 - 笔记 作者(Author): 郑龙浩 / 仟濹(CSND账号名) 洛谷【排序】题单合集 一、排序算法都有… 1. 简单排序算法 这些算法通常是基础的排序方法&#xff0c;容易理解和实现&#xff0c;但效率较低&#xff0c;适用于数据量较小的情况…

MySQL--》如何在SQL中巧妙运用函数与约束,优化数据处理与验证?

目录 函数使用 字符串函数 数值函数 日期函数 流程函数 约束 函数使用 函数是指一段可以直接被另一段程序调用的程序或代码&#xff0c;在mysql当中有许多常见的内置函数&#xff0c;接下来开始对这些内置函数及其作用进行简单的讲解和使用&#xff1a; 字符串函数 my…

归有光,情感与真实的独行者

归有光&#xff0c;字熙甫&#xff0c;号震川&#xff0c;生于明孝宗弘治十年&#xff08;公元1507年&#xff09;&#xff0c;卒于明穆宗隆庆五年&#xff08;公元1571年&#xff09;&#xff0c;享年64岁。他是中国明代著名的散文家、文学家和史学家&#xff0c;其散文风格清…

Python + OpenCV 系列:图像阈值处理

文章目录 引言 1. 阈值处理的基本概念2. OpenCV 中的阈值处理3. 常见的阈值类型3.1 二值化阈值3.2 反向二值化阈值3.3 截断阈值3.4 平滑阈值 4. 自适应阈值5. Otsu’s 阈值法6. 阈值处理的应用场景7. 总结 引言 图像阈值处理是计算机视觉和图像处理中一种非常基础而重要的技术…

计算机网络-Wireshark探索ARP

使用工具 Wiresharkarp: To inspect and clear the cache used by the ARP protocol on your computer.curl(MacOS)ifconfig(MacOS or Linux): to inspect the state of your computer’s network interface.route/netstat: To inspect the routes used by your computer.Brows…

Vue3小兔鲜电商项目

创建项目 npm install 装包

【NLP 12、深度学习15条调参经验】

反正是绚烂&#xff0c;反正是到来 反正是背负慢慢凋残的孤独 耀眼的孤独&#xff0c;义无反顾的孤独 —— 24.12.9 深度学习15条调参经验 1.调参 调参是锦上添花的事&#xff0c;而底线取决于模型的选择和数据的清洗 2.关于model ① 尽量不要自己手写模型&#xff0c;找一…

美畅物联丨视频接入网关如何配置 HTTPS 证书

在安防领域&#xff0c;视频接入网关&#xff08;Video Access Gateway&#xff0c;VAG&#xff09;是视频监控系统的重要组成部分&#xff0c;其职责是把视频数据从前端设备传输至后端服务器。配置HTTPS证书后&#xff0c;可对视频流进行加密传输&#xff0c;避免数据在网络传…

fastcam编程套料软件

Fastcam是一款功能强大的专业软件&#xff0c;以下是对它的具体介绍&#xff1a; 基本信息 • Fastcam自1982年推出首个交互式CNC编程和套料系统后&#xff0c;一直不断更新发展. • 它是为数控火焰、等离子、激光和水射流切割机等数控切割机开发的编程套料软件. 主要功能模…

【大语言模型】LangChain ModelsIO与Models I/O Promopts详解

【大语言模型】LangChain ModelsIO与Prompts详解 一、LangChain ModelsIO1、简介2、Models I/O 的应用场景3、Models I/O 主要模块3.1、Prompts3.2、Modelsa、MESSAGES 类型 3.3、Output Parsers 二、LangChain ModelsIO Prompts1、简介2、Prompts 的优点3、实战示例3.1、Promp…

MicroBlaze软核开发(三):DDR + FLASH

实现功能&#xff1a;使用 MicroBlaze软核&#xff0c;配置 DDR、Flash 接口&#xff0c;并将程序烧写固化到Flash&#xff0c;每次启动 FPGA 时自动运行 Flash 中的程序 。 Vivado版本&#xff1a;2018.3 FPGA&#xff1a;Xilinx Artix-7 系 目录 引言 Vivado部分&#xff1…

Springboot(五十三)SpringBoot3整合redisson

前边我们有记录Springboot2/3整合redis的方式。是基于Springboot默认集成的Lettuce客户端实现的。 今天我们在项目中集成的redission是在Lettuce之上构建的redis客户端。 Redisson:一个在Jedis和Lettuce之上构建的Redis客户端。提供了一系列分布式Java对象和服务,比如:分布式…