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

news2025/7/19 18:46:25

专栏:算法的魔法世界

个人主页:手握风云

目录

一、搜索算法

二、回溯算法

三、例题讲解

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.coloradmin.cn/o/2379339.html

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

相关文章

BMVC2023 | 多样化高层特征以提升对抗迁移性

Diversifying the High-level Features for better Adversarial Transferability 摘要-Abstract引言-Introduction相关工作-Related Work方法-Methodology实验-Experiments结论-Conclusion 论文链接 GitHub链接 本文 “Diversifying the High-level Features for better Adve…

有哪些GIF图片转换的开源工具

以下是关于GIF图片转换的开源工具的详细总结,涵盖功能特点、适用场景及用户评价: 1. FFmpeg 功能特点: 作为开源命令行工具,FFmpeg支持视频转GIF、调整帧率、分辨率、截取片段等操作,可通过脚本批量处理。适用场景: 适合开发者或技术用户进行高效批处理,常用于服务器端自…

C++—特殊类设计设计模式

目录 C—特殊类设计&设计模式1.设计模式2.特殊类设计2.1设计一个无法被拷贝的类2.2设计一个只能在堆上创建对象的类2.3设计一个只能在栈上创建对象的类2.4设计一个类&#xff0c;无法被继承2.5设计一个类。这个类只能创建一个对象【单例模式】2.5.1懒汉模式实现2.5.2饿汉模…

Android 手写签名功能详解:从原理到实践

Android 手写签名功能详解 1. 引言2. 手写签名核心实现&#xff1a;SignatureView 类3. 交互层实现&#xff1a;MainActivity 类4. 布局与配置5. 性能优化与扩展方向 1. 引言 在电子政务、金融服务等移动应用场景中&#xff0c;手写签名功能已成为提升用户体验与业务合规性的关…

Level2.8蛇与海龟(游戏)

#小龟快跑游戏 输入难度(1-5),蛇追到龟&#xff0c;游戏结束 #分析问题&#xff1a;从局部>整体 #游戏画面&#xff1a;创建画笔(海龟蛇)>1.海龟移动(键盘控制)>2.蛇(自动追踪&#xff0c;海龟位置)>3.海龟(限定范围&#xff0c;防止跑出画布之外)>4.游戏&…

【Android构建系统】如何在Camera Hal的Android.bp中选择性引用某个模块

背景描述 本篇文章是一个Android.bp中选择性引用某个模块的实例。 如果是Android.mk编译时期&#xff0c;在编译阶段通过某个条件判断是不是引用某个模块A, 是比较好实现的。Android15使用Android.bp构建后&#xff0c;要想在Android.bp中通过自定义的一个变量或者条件实现选…

【Canvas与诗词】醉里挑灯看剑 梦回吹角连营

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>醉里挑灯看剑梦回吹角连营 Draft1</title><style type"…

实现视频分片上传 OSS

访问 OSS 有两种方式&#xff0c;本文用到的是使用临时访问凭证上传到 OSS&#xff0c;不同语言版本的代码参考&#xff1a; 使用STS临时访问凭证访问OSS_对象存储(OSS)-阿里云帮助中心 1.安装并使用 首先我们要安装 OSS&#xff1a; npm install ali-oss --save 接着我们…

网络I/O学习(一)

一、什么是网络IO&#xff1f; 就是客户端和服务端之间的进行通信的通道(fd)。 二、网络IO通信步骤 1、建立套接字 int socketfd socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr; servaddr.sin_family AF_INET; servaddr.sin_addr.s_addr htonl(INADDR_A…

Selenium-Java版(css表达式)

css表达式 前言 根据 tag名、id、class 选择元素 tag名 #id .class 选择子元素和后代元素 定义 语法 根据属性选择 验证CSS Selector 组选择 按次序选择子节点 父元素的第n个子节点 父元素的倒数第n个子节点 父元素的第几个某类型的子节点 父元素的…

产品更新丨谷云科技 iPaaS 集成平台 V7.5 版本发布

五月&#xff0c;谷云科技 iPaaS 集成平台保持月度更新&#xff0c; V7.5 版本于近日正式发布。我们一起来看看新版本有哪些升级和优化。 核心新增功能&#xff1a;深化API治理&#xff0c;释放连接价值 API网关&#xff1a;全链路可控&#xff0c;精准管控业务状态 业务状态…

深度学习让鱼与熊掌兼得

通常,一个大的复杂的模型的loss会低,但是拟合方面不够,小的模型在拟合方面更好,但是loss高,我们可以通过深度学习来得到一个有着低loss的小模型 我们之前学过,peacewise linear可以用常数加上一堆这个阶梯型函数得到,然后因为peacewise linear可以逼近任何function,所以理论上…

TDuckX 2.6 正式发布|API 能力开放,核心表单逻辑重构,多项实用功能上线。

大家好&#xff0c;TDuckX 2.6 已正式发布。 本次更新以可集成性提升、数据处理能力增强和交互体验优化为核心&#xff0c;新增了包括 新增OpenAPI 模块、表单数据批量修改、字段导出分列 等多个面向开发者和实际业务落地场景的功能。 我们也重构了部分底层逻辑模块&#xff…

JAVA EE(进阶)_进阶的开端

别放弃浸透泪水的昨天&#xff0c;晨光已为明天掀开新篇 ——陳長生. ❀主页&#xff1a;陳長生.-CSDN博客❀ &#x1f4d5;上一篇&#xff1a;JAVA EE_HTTP-CSDN博客 1.什么是Java EE Java EE&#xff08;Java Pla…

ArcGIS Pro调用多期历史影像

一、访问World Imagery Wayback&#xff0c;基本在我国范围 如下图&#xff1a; 二、 放大到您感兴趣的区域 三、 查看影像版本信息 点击第二步的按钮后&#xff0c;便可跳转至World Imagery (Wayback 2025-04-24)的相关信息。 四 、点击上图影像版本信息&#xff0c;页面跳转…

组态王|组态王中如何添加西门子1200设备

哈喽,你好啊,我是雷工! 最近使用组态王采集设备数据,设备的控制器为西门子的1214CPU, 这里边实施边记录,以下为在组态王中添加西门子1200PLC的笔记。 1、新建 在组态王工程浏览器中选择【设备】→点击【新建】。 2、选择设备 和设备建立通讯要通过对应的设备驱动。 在…

6.2.2邻接表法-图的存储

知识总览&#xff1a; 为什么要用邻接表 因为邻接矩阵的空间复杂度高(O(n))&#xff0c;且不适合边少的稀疏图&#xff0c;所以有了邻接表 用代码表示顶点、图 声明顶点图信息 声明顶点用一维数组存储各个顶点的信息&#xff0c;一维数组字段包括2个&#xff0c;每个顶点的…

C++23 放宽范围适配器以允许仅移动类型(P2494R2)

文章目录 引言背景与动机提案内容与实现细节提案 P2494R2实现细节编译器支持 对开发者的影响提高灵活性简化代码向后兼容性 示例代码总结 引言 C23 标准中引入了许多重要的改进&#xff0c;其中一项值得关注的特性是放宽范围适配器&#xff08;range adaptors&#xff09;以允…

【技海登峰】Kafka漫谈系列(十一)SpringBoot整合Kafka之消费者Consumer

【技海登峰】Kafka漫谈系列(十一)SpringBoot整合Kafka之消费者Consumer spring-kafka官方文档: https://docs.spring.io/spring-kafka/docs/2.8.10/reference/pdf/spring-kafka-reference.pdf KafkaTemplate API: https://docs.spring.io/spring-kafka/api/org/springframe…

WebRTC技术下的EasyRTC音视频实时通话SDK,助力车载通信打造安全高效的智能出行体验

一、方案背景​ 随着智能交通与车联网技术的飞速发展&#xff0c;车载通信在提升行车安全、优化驾驶体验以及实现智能交通管理等方面发挥着越来越重要的作用。传统的车载通信方式在实时性、稳定性以及多媒体交互能力上存在一定局限&#xff0c;难以满足现代车载场景日益复杂的…