二叉树深搜:在算法森林中寻找路径

专栏:算法的魔法世界

个人主页:手握风云

目录

一、搜索算法

二、回溯算法

三、例题讲解

3.1. 计算布尔二叉树的值

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

3.3. 二叉树剪枝

3.4. 验证二叉搜索树

3.5. 二叉搜索树中第 K 小的元素

3.6. 二叉树的所有路径


一、搜索算法

  • BFS和DFS

        广度优先搜索(BFS)和深度优先搜索(DFS)是两种常用的图和树的遍历算法,遍历是形式,目的是搜索,在某种形式上,遍历算法与搜索算法可以等价。

        BFS 的核心思想是从一个节点开始,逐层遍历所有可达的节点,直到找到目标节点或遍历完所有节点。DFS 的核心思想是从一个节点开始,沿着一条路径尽可能深地搜索,直到无法继续前进时,再回溯到上一个节点,继续搜索其他路径。

  • 暴搜

        暴力搜索是一种简单的搜索方式,通过暴力枚举所有的情况来找出结果,通常基于BFS和DFS实现。暴力搜索通常应用于用于解决那些没有明显规律或优化方法的问题,尤其是在数据规模较小时,但有时效率会特别低。

二、回溯算法

        回溯算法是一种通过递归搜索所有可能的候选解来找出所有解的算法,它没有那么神秘,本质上其实就是DFS。回溯算法通常可用于迷宫求解,如下图所示,从起点进入并走出迷宫,按照深搜的思路,沿着一条路径走。如果是死胡同,就回溯走另一条路径,再次按照DFS,递归搜索和回溯找到迷宫出口。如果在搜索之前,我们知道这个答案不是我们想要的,我们就直接可以舍弃,这个过程就叫剪枝。

三、例题讲解

3.1. 计算布尔二叉树的值

        本题中给出的是完整二叉树,要么没有节点,要么左右孩子节点都有。其中叶子节点储存的是真值,非叶子节点储存的是逻辑与、或。我们以示例1为例,0合取1,结果为0;0析取1,结果为1,返回真。

        宏观角度:从上面的示例中我们可以得出,这棵树是从下往上推导的。当我们只看根节点时,我们得知道左子树的值,同时也得知道右子树的值,然后在根节点这一层进行整合。而关于求出左右子树的布尔值,与前面的问题是一样的,只需要传入一个引用,即可返回布尔值,这就是递归方法头与方法体的设计。当我们遍历到叶子节点时,不需要判断它的左右子树是否为空,只需要进行逻辑运算即可,这同时也是递归的出口。

        细节角度:因为计算过程是从下往上,我们可以看作是二叉树的后序遍历。从根节点出发,先去遍历左子树,当遇到叶子节点时,返回该节点的布尔值,然后回溯到它的父亲节点,然后遍历右子树,再回溯父亲节点并遍历自身。

        完整代码实现:

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public boolean evaluateTree(TreeNode root) {if (root.left == null) {return root.val == 1;}boolean left = evaluateTree(root.left);boolean right = evaluateTree(root.right);return root.val == 2 ? (left || right) : (left && right);}
}

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

        需要计算出从根节点出发到所有叶子节点的每一条路径所表示的数字,然后将这些数字相加,得到的总和就是最终要求的结果。即要找出二叉树中所有从根到叶子的路径所对应的数字,并把它们累加起来。

        以下图为例,当我们递归到“9”这一层时,我们就需要把“4”先传进来,计算得到49。再把"49"继续向下递归,计算得到495、491,然后返回,直到返回到根结点。那递归的方法头可以设计两个参数,一个根结点和一个路径和presum,返回值为根结点所连接的叶子结点的数字之和。方法体的执行下图中的4步。递归的出口则为遇到叶子结点时,返回路径之和。

        完整代码实现:

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public int sumNumbers(TreeNode root) {return dfs(root,0);}private int dfs(TreeNode root,int preSum) {preSum = preSum * 10 + root.val;if (root.left == null && root.right == null) {return preSum;}int ret = 0;if (root.left != null) ret += dfs(root.left,preSum);if (root.right != null) ret += dfs(root.right,preSum);return ret;}
}

3.3. 二叉树剪枝

        题目要求移除所有不包含1的子树,也就是说,如果一个节点没有任何子节点且其值为0,那么这个节点就应该被删除。

        如果我们想剪掉某一棵子树,必须先知道左右子树的信息,所以我们一定是要采用后序遍历来实现。当我们遍历到某一节点时,如果发现左右子树均为空,并且节点值为0,那我们就可以把这个节点剪掉。而我们返回它的父亲节点时,需要加一个返回值将其置为空,然后继续判断。如果节点值为1,那我们就直接返回它的父亲节点就可以了。那我们设计递归方法的时候,只需传入一个根节点,然后去遍历它的左右子树,根据返回值来判断是否该剪掉。

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public TreeNode pruneTree(TreeNode root) {if (root == null) {return null;}root.left = pruneTree(root.left);root.right = pruneTree(root.right);if (root.left == null && root.right == null && root.val == 0) {root = null;}return root;}
}

3.4. 验证二叉搜索树

        根据前面学过的知识,对一棵二叉搜索树进行中序遍历,得到的结果是一个有序序列,所以我们可以先定义一个全局变量prev,当遍历到某一个节点时,用prev存储前一个节点值,如下图所示,根据中序遍历,当我们遍历到1时,将prev赋值为1,然后进行比较,后面的数如果越来越大,那就是二叉搜索树。当遍历到19时,会发现19比20小,整棵树就不是二叉搜索树。那么我们就可以通过剪枝,省去处理右子树的过程,直接向上返回一个false。

        完整代码实现:

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {long prev = Long.MIN_VALUE;public boolean isValidBST(TreeNode root) {if (root == null) return true;boolean left = isValidBST(root.left);//剪枝if(left == false) return false;boolean cur = false;if (root.val > prev) cur = true;prev = root.val;boolean right = isValidBST(root.right);return left && cur && right;}
}

3.5. 二叉搜索树中第 K 小的元素

        仿照上一题的思路,利用两个全局变量,其中一个变量是在中序遍历时充当计数器,另一个变量去标记第K小的结果。进行中序遍历,当遍历到第一个节点时,count--,直到count=0时,说明已经到了第k个节点,此时进行剪枝,把ret更新为17,无需遍历其它子树。

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {int count;int ret;public int kthSmallest(TreeNode root, int k) {count = k;InOrder(root);return ret;}public void InOrder(TreeNode root) {if (root == null || count == 0) return;InOrder(root.left);count--;if (count == 0) ret = root.val;if (count == 0) return;InOrder(root.right);}
}

3.6. 二叉树的所有路径

        给定一个二叉树的根节点,然后找出所有从根节点到叶子节点的路径。每条路径都应该以字符串的形式返回,路径中的节点值之间用箭头“->”连接。

        我们先弄一个字符串变量path,在进行前序遍历的时候,记录每个路径的字符串,当遇到叶子节点时,丢进顺序表List<String> ret中。但如果我们遍历完一条路径后,回溯就会出现问题,比如我们遍历完"1->2->4",回溯到节点2时,也会返回这个结果,所以我们还需要进行恢复现场的操作,把字符4移除。当我们把节点2处理完返回到节点1时,我们需要移除的是"2->",所以这个过程就会非常麻烦。我们可以把这个变量放在递归方法的参数里,那我们的方法头就可以设计成DFS(root,path)。当递归方法执行的时候,如果是叶子节点,不需要加上"->";如果不是叶子节点,需要加上"->"。当某个节点的左子树为空而右子树不为空,那就不需要执行DFS(root.right,path),直接返回。

        完整代码实现:

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {List<String> ret;public List<String> binaryTreePaths(TreeNode root) {ret = new ArrayList<>();DFS(root,new StringBuffer());return ret;}public void DFS(TreeNode root,StringBuffer _path) {StringBuffer path = new StringBuffer(_path);path.append(Integer.toString(root.val));if (root.left == null && root.right == null) {ret.add(path.toString());return;}path.append("->");if (root.left != null) DFS(root.left,path);if (root.right != null) DFS(root.right,path);}
}

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

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

相关文章

企业级AI搜索解决方案:阿里云AI搜索开放平台

随着信息技术的飞速发展&#xff0c;搜索引擎作为信息获取的重要工具&#xff0c;扮演着不可或缺的角色。阿里云 AI 搜索开放平台以其强大的技术支持和灵活的开放性&#xff0c;持续为用户提供高效的搜索解决方案。 一、阿里云 AI 搜索开放平台 一站式的 AI 搜索开放平台作为…

自动驾驶中的预测控制算法:用 Python 让无人车更智能

自动驾驶中的预测控制算法:用 Python 让无人车更智能 自动驾驶技术近年来取得了令人惊叹的进步,AI 与边缘计算的结合让车辆能够实时感知环境、规划路径并执行驾驶决策。其中,预测控制(Model Predictive Control,MPC) 作为一种先进的控制算法,凭借其对未来驾驶行为的优化…

量子计算机超越超级计算机——它们解决了哪些问题?

“ 南加州大学的研究人员取得了重大突破&#xff0c;证明量子计算机在解决某些复杂问题时甚至可以胜过最快的超级计算机。” 量子退火最终显示出扩展优势&#xff0c;得益于错误抑制的量子处理&#xff0c;它比传统超级计算机提供更快、接近最优的解决方案。 南加州大学的研究人…

Java虚拟机 -方法调用

方法调用 方法调用静态链接动态链接案例虚方法与非虚方法虚方法&#xff08;Virtual Method&#xff09;非虚方法&#xff08;Non-Virtual Method&#xff09; 方法返回地址 方法调用 我们编写Java程序的时候&#xff0c;我们自己写的类通常不仅仅是调用自己本类的方法。调用别…

【 开源:跨平台网络数据传输的万能工具libcurl】

在当今这个互联互通的世界中&#xff0c;数据在各种设备和平台之间自由流动&#xff0c;而 libcurl&#xff0c;就像一把跨平台的万能工具&#xff0c;为开发者提供了处理各种网络数据传输任务所需的强大功能。它不仅是一个库&#xff0c;更是一种通用的解决方案&#xff0c;可…

ElasticSearch 8.x 快速上手并了解核心概念

目录 核心概念概念总结 常见操作索引的常见操作常见的数据类型指定索引库字段类型mapping查看索引库的字段类型最高频使用的数据类型 核心概念 在新版Elasticsearch中&#xff0c;文档document就是一行记录(json)&#xff0c;而这些记录存在于索引库(index)中, 索引名称必须是…

优化 CRM 架构,解锁企业竞争力密码

引言 “在所有企业面临的挑战中&#xff0c;客户关系管理无疑是最为关键的一环。” —— 彼得德鲁克 在数字化浪潮席卷的当下&#xff0c;企业面临着前所未有的机遇与挑战。客户关系管理&#xff08;CRM&#xff09;作为企业运营的核心环节&#xff0c;其架构的优劣直接影响着…

深入理解Docker和K8S

深入理解Docker和K8S Docker 是大型架构的必备技能&#xff0c;也是云原生核心。Docker 容器化作为一种轻量级的虚拟化技术&#xff0c;其核心思想&#xff1a;将应用程序及其所有依赖项打包在一起&#xff0c;形成一个可移植的单元。 容器的本质是进程&#xff1a; 容器是在…

list.forEach(s -> countService.refreshArticleStatisticInfo(s.getId())); 讲解一下语法

这段代码使用了Java中的forEach方法结合Lambda表达式来遍历一个列表&#xff0c;并对列表中的每个元素执行特定操作。具体来说&#xff0c;它会遍历列表中的每一个元素&#xff0c;并调用countService.refreshArticleStatisticInfo(s.getId())方法来刷新每个文章的统计信息。下…

AI开发者的算力革命:GpuGeek平台全景实战指南(大模型训练/推理/微调全解析)

目录 背景一、AI工业化时代的算力困局与破局之道1.1 中小企业AI落地的三大障碍1.2 GpuGeek的破局创新1.3 核心价值 二、GpuGeek技术全景剖析2.1 核心架构设计 三、核心优势详解‌3.1 优势1&#xff1a;工业级显卡舰队‌‌‌3.2 优势2&#xff1a;开箱即用生态‌3.2.1 预置镜像库…

05算法学习_59. 螺旋矩阵 II

05算法学习_59. 螺旋矩阵 II 05算法学习_59. 螺旋矩阵 II题目描述&#xff1a;个人代码&#xff1a;学习思路&#xff1a;第一种写法&#xff1a;题解关键点&#xff1a; 个人学习时疑惑点解答&#xff1a; 05算法学习_59. 螺旋矩阵 II 力扣题目链接: 59. 螺旋矩阵 II 题目描…

JDK7Hashmap的头插法造成的环问题

单线程下的扩容 多线程下的扩容 next&#xff1d;e 然后e的next变成e

JAVA|后端编码规范

目录 零、引言 一、基础 二、集合 三、并发 四、日志 五、安全 零、引言 规范等级&#xff1a; 【强制】&#xff1a;强制遵守&#xff0c;来源于线上历史故障&#xff0c;将通过工具进行检查。【推荐】&#xff1a;推荐遵守&#xff0c;来源于日常代码审查、开发人员反馈…

2025-05-21 Python深度学习5——数据读取

文章目录 1 数据准备2 Dataset2.1 自定义 Dataset2.2 使用示例 3 TensorBoard3.1 安装3.2 标量可视化&#xff08;Scalars&#xff09;3.3 图像可视化&#xff08;Images&#xff09;3.4 其他常用功能 4 transform4.1 ToTensor()4.2 Normalize()4.3 Resize()4.4 Compose()4.5 C…

5月21日学习笔记

MYSQL三层结构 表1 数据库DB1 表2 数据库管理系统 客户端命令终端&#xff08;Dos&#xff09; DBMS 数据库DB2 表1 表2 数据库………. Mysql数据库-表的本质仍然是文件 表的一行称之为一条记录->在java程序中一行记录往往使用对象表示 SQL语…

二十、面向对象底层逻辑-ServiceRegistry接口设计集成注册中心

一、服务治理的基石接口 在微服务架构中&#xff0c;服务实例的动态注册与发现是保证系统弹性的关键机制。Spring Cloud Commons模块通过ServiceRegistry与Registration接口定义了服务注册的标准化模型&#xff0c;为不同服务发现组件&#xff08;Eureka、Consul、Nacos等&…

DeepSeek:以开源之力,引领AI技术新风潮

在年春节&#xff0c;大语言模型DeepSeek如同一枚震撼弹&#xff0c;在全球范围内引发了轰动&#xff0c;成功“破圈”&#xff0c;将中国的人工智能&#xff08;AI&#xff09;技术成果推向了世界舞台。 开源策略&#xff1a;打破技术壁垒 在AI行业&#xff0c;OpenAI等巨头…

完整改进RIME算法,基于修正多项式微分学习算子Rime-ice增长优化器,完整MATLAB代码获取

1 简介 为了有效地利用雾状冰生长的物理现象&#xff0c;最近开发了一种优化算法——雾状优化算法&#xff08;RIME&#xff09;。它模拟硬雾状和软雾状过程&#xff0c;构建硬雾状穿刺和软雾状搜索机制。在本研究中&#xff0c;引入了一种增强版本&#xff0c;称为修改的RIME…

PyTorch可视化工具——使用Visdom进行深度学习可视化

文章目录 前置环境Visdom安装并启动VisdomVisdom图形APIVisdom静态更新API详解通用参数说明使用示例Visdom动态更新API详解1. 使用updateappend参数2. ~~使用vis.updateTrace方法~~3. 完整训练监控示例 Visdom可视化操作散点图plot.scatter()散点图案例线性图vis.line()vis.lin…

Java使用Collections集合工具类

1、Collections 集合工具类 Java 中的 Collections 是一个非常有用的工具类&#xff0c;它提供了许多静态方法来操作或返回集合。这个类位于 java.util 包中&#xff0c;主要包含对集合进行操作的方法&#xff0c;比如排序、搜索、线程安全化等。 Java集合工具类的使用&#x…