递归、搜索与回溯算法(专题二:深搜)

news2024/9/9 5:27:47

往期文章(希望小伙伴们在看这篇文章之前,看一下往期文章)

(1)递归、搜索与回溯算法(专题零:解释回溯算法中涉及到的名词)【回溯算法入门必看】-CSDN博客

(2)递归、搜索与回溯算法(专题一:递归)-CSDN博客

 深搜是实现递归的一种方式,接下来我们之间从题入手,深入浅出地了解深搜吧!

目录

1. 计算布尔二叉树的值

2. 求根结点到叶结点的数字之和

3. 二叉树剪枝

4. 验证二叉搜索树

5. 二叉搜索树中第k小的元素

​​6. 二叉树的所有路径


1. 计算布尔二叉树的值

力扣题目链接

 

解析: 

(1)函数头设计

需要根结点,故函数头为:dfs(TreeNode root);

(2)函数体设计(无条件相信这个“黑盒”,他能帮你将每个相同的子问题稳妥地解决!!!)

① 接收左子树传过来的值:boolean leftTree = dfs(root.left);       

② 接收右子树传过来的值:boolean rightTree = dfs(root.right);

③ leftTree ——> root.val <—— rightTree 得到最终结果。

(3)递归出口

到叶子结点就是应该结束递归,开始回溯。

    public boolean evaluateTree(TreeNode root) {
        //递归出口
        //到了叶子结点
        if(root.left == null){
            if(root.val == 0)
                return false;
            else
                return true;
        }
        //开始递归
        boolean leftTree = evaluateTree(root.left);
        boolean rightTree = evaluateTree(root.right);
        if(root.val == 2){
            return leftTree || rightTree;
        }else{
            return leftTree && rightTree;
        }
    }

2. 求根结点到叶结点的数字之和

力扣题目链接

解析:

前序遍历按照根节点、左⼦树、右⼦树的顺序遍历⼆叉树的所有节点,通常⽤于⼦节点的状态依赖于⽗节点状态的题⽬。
在前序遍历的过程中,我们可以往左右⼦树传递信息,并且在回溯时得到左右⼦树的返回值。递归函数可以帮我们完成两件事:
(1)将⽗节点的数字与当前节点的信息整合到⼀起,计算出当前节点的数字,然后传递到下⼀层进⾏递
归;
(2)当遇到叶⼦节点的时候,就不再向下传递信息,⽽是将整合的结果向上⼀直回溯到根节点。
在递归结束时,根节点需要返回的值也就被更新为了整棵树的数字和。

递归函数设计:int dfs(TreeNode root,int num)
(1)返回值:当前⼦树计算的结果(数字和);

(2)参数num:递归过程中往下传递的信息(⽗节点的数字);
(3)函数作⽤:整合⽗节点的信息与当前节点的信息计算当前节点数字,并向下传递,在回溯时返回当前⼦树(当前节点作为⼦树根节点)数字和。

递归函数流程:
(1)当遇到空节点的时候,说明这条路从根节点开始没有分⽀,返回0;
(2)结合⽗节点传下的信息以及当前节点的val,计算出当前节点数字sum;
(3)如果当前结点是叶⼦节点,直接返回整合后的结果sum;
(4)如果当前结点不是叶⼦节点,将?sum?传到左右⼦树中去,得到左右⼦树中节点路径的数字和,然后相加后返回结果。

① 将自身的值并入;②  将自己本身的数字并入后走左子树;③ 将自己本身的数字并入后走右子树;④ 左右子树的结果,然后向上返回。

    public int sumNumbers(TreeNode root) {
        return dfs(root,0);//从个位数开始
    }
    public int dfs(TreeNode root,int perSum){
        //一:将自身的值并入
        perSum = perSum * 10 + root.val;
        //四:递归出口:看看是不是叶子结点了,如果是,就向上返回
        if(root.left == null && root.right == null){
            return perSum;
        }
        //二:走左子树
        int ret = 0;
        if(root.left != null){
            ret += dfs(root.left,perSum);
        }
        //三:走右子树
        if(root.right != null){
            ret += dfs(root.right,perSum);
        }
        return ret;
    }

3. 二叉树剪枝

力扣题目链接

解析:

后序遍历按照左⼦树、右⼦树、根节点的顺序遍历⼆叉树的所有节点,通常⽤于⽗节点的状态依赖于⼦节点状态的题⽬。
(1)如果我们选择从上往下删除,我们需要收集左右⼦树的信息,这可能导致代码编写相对困难。然⽽,通过观察我们可以发现,如果我们先删除最底部的叶⼦节点,然后再处理删除后的节点,最终的结果并不会受到影响。
(2)因此,我们可以采⽤后序遍历的⽅式来解决这个问题。在后序遍历中,我们先处理左⼦树,然后处理右⼦树,最后再处理当前节点。在处理当前节点时,我们可以判断其是否为叶⼦节点且其值是否为0。如果满⾜条件,我们可以删除当前节点。
• 需要注意的是,在删除叶⼦节点时,其⽗节点很可能会成为新的叶⼦节点。因此,在处理完⼦节点后,我们仍然需要处理当前节点。这也是为什么选择后序遍历的原因(后序遍历⾸先遍历到的⼀定是叶⼦节点)。
• 通过使⽤后序遍历,我们可以逐步删除叶⼦节点,并且保证删除后的节点仍然满⾜删除操作的要
求。这样,我们可以较为⽅便地实现删除操作,⽽不会影响最终的结果。
• 若在处理结束后所有叶⼦节点的值均为1,则所有⼦树均包含1,此时可以返回。

递归函数头设计:void dfs(TreeNode root)
(1)返回值:⽆;
(2)参数:当前需要处理的节点;
(3)函数作⽤:判断当前节点是否需要删除,若需要删除,则删除当前节点。

后序遍历的主要流程(函数体):

(1)递归出⼝:当传⼊节点为空时,不做任何处理;
(2)递归处理左⼦树;
(3)递归处理右⼦树;
(4)处理当前节点:判断该节点是否为叶⼦节点(即左右⼦节点均被删除,当前节点成为叶⼦节点),并且节点的值为0:

  • 如果是,就删除掉;
  • 如果不是,就不做任何处理。
    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;
    }

4. 验证二叉搜索树

力扣题目链接

 

解析:

中序遍历按照左⼦树、根节点、右⼦树的顺序遍历⼆叉树的所有节点,通常⽤于⼆叉搜索树相关题
⽬。
(1)如果⼀棵树是⼆叉搜索树,那么它的中序遍历的结果⼀定是⼀个严格递增的序列。
(2)因此,我们可以初始化⼀个⽆穷⼩的全区变量,⽤来记录中序遍历过程中的前驱结点。那么就可以在中序遍历的过程中,先判断是否和前驱结点构成递增序列,然后修改前驱结点为当前结点,传⼊下⼀层的递归中。 

算法流程:
(1)初始化⼀个全局的变量prev,⽤来记录中序遍历过程中的前驱结点的val;
(2)中序遍历的递归函数中:
① 设置递归出⼝:root == null 的时候,返回true;(叶子结点本身就是一棵二叉搜索树)
② 先递归判断左⼦树是否是⼆叉搜索树,⽤left标记;
③ 然后判断当前结点是否满⾜⼆叉搜索树的性质;
▪ 如果当前结点的val⼤于prev,说明满⾜条件,将prev改为root.val;
▪ 如果当前结点的val⼩于等于prev,说明不满⾜条件,return false;
最后递归判断右⼦树是否是⼆叉搜索树,⽤right标记;
(3)只有当left和right都是true的时候,才返回true。

    long prev = Long.MIN_VALUE;//存放上一个结点的值
    public boolean isValidBST(TreeNode root) {
        if(root == null)
            return true;
        
        //递归判断左子树
        boolean left = isValidBST(root.left);
        if(prev < root.val)
            prev = root.val;
        //判断当前节点是否为二叉搜索树,右边就不需要搞
        else
            return false;
        //剪枝,剪掉右子树的判断
        if(left == false)
            return false;
        //递归判断右子树,告诉父节点,我不是二叉搜索树,你也不是
        boolean right = isValidBST(root.right);
        if(left == true && right == true)
            return true;
        else
            return false;
    }

 

5. 二叉搜索树中第k小的元素

力扣题目链接 

 

解析:

中序遍历 + 计数器剪枝

我们可以根据中序遍历的过程,只需扫描前k个结点即可。
因此,我们可以创建⼀个全局的计数器count,将其初始化为k,每遍历⼀个节点就将count--。直到某次递归的时候,count的值等于1,说明此时的结点就是我们要找的结果。

    int ret = 0;//用来存储最终结果
    int count;//用来表示要找第几个结点
    public int kthSmallest(TreeNode root, int k) {
        count = k;
        dfs(root);
        return ret;
    }
    public void dfs(TreeNode root){
        if(root == null)
            return;
        dfs(root.left);
        count--;
        if(count == 0){
            ret = root.val;
        }
        dfs(root.right);
    }

 

6. 二叉树的所有路径

力扣题目链接 

 

解析:

使⽤深度优先遍历(DFS)求解。
路径以字符串形式存储,从根节点开始遍历,每次遍历时将当前节点的值加⼊到路径中,如果该节点为叶⼦节点,将路径存储到结果中。否则,将"->"加⼊到路径中并递归遍历该节点的左右⼦树。
定义⼀个结果数组,进⾏递归。递归具体实现⽅法如下:
(1)如果当前节点不为空,就将当前节点的值加⼊路径path中,否则直接返回;
(2)判断当前节点是否为叶⼦节点,如果是,则将当前路径加⼊到所有路径的存储数组paths中;
(3)否则,将当前节点值加上"->"作为路径的分隔符,继续递归遍历当前节点的左右⼦节点;
(4)返回结果数组;


注:特别地,我们可以只使⽤⼀个字符串存储每个状态的字符串,在递归回溯的过程中,需要将路径中的当前节点移除,以回到上⼀个节点。

具体实现⽅法如下:
(1)定义⼀个结果数组和⼀个路径数组。
(2)从根节点开始递归,递归函数的参数为当前节点、结果数组和路径数组。
① 如果当前节点为空,返回。
② 将当前节点的值加⼊到路径数组中。
③ 如果当前节点为叶⼦节点,将路径数组中的所有元素拼接成字符串,并将该字符串存储到结果
数组中。
④ 递归遍历当前节点的左⼦树。
⑤ 递归遍历当前节点的右⼦树。
⑥ 回溯,将路径数组中的最后⼀个元素移除,以返回到上⼀个节点。

    List<String> ret;
    public List<String> binaryTreePaths(TreeNode root) {
        ret = new ArrayList<>();
        dfs(root,new StringBuffer());
        return ret;
    }
    public void dfs(TreeNode root,StringBuffer curPath){
        //起恢复现场的作用
        StringBuffer path = new StringBuffer(curPath);
        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/1399126.html

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

相关文章

linux基础学习(5):yum

yum是为了解决rpm包安装依赖性而产生的一种安装工具 1.yum源 1.1配置文件位置 yum源的配置文件在/etc/yum.repos.d/中 *Base源是网络yum源&#xff0c;也就是需要联网才能使用的yum源。默认情况下&#xff0c;系统会使用Base源 *Media源是光盘yum源&#xff0c;是本地yum源…

openjdk源码了解

openjdk给出debug配置选项&#xff0c;common/autoconf/jdk-options.m4 AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_LEVEL], [################################################################################# Set the debug level# release: no debug information, all opti…

Jenkins实现CICD(1)_Windows10 安装Jenkins

文章目录 一、打开Jenkins官网&#xff0c;下载安装包二、安装Jenkins三、JAVA环境_JDK17下载安装&#xff08;Windows版&#xff09;四、将jdk-17添加到系统环境变量五、jenkins关联jdk-17六、安装常用插件(例如&#xff1a;git、gitlab、钉钉) 一、打开Jenkins官网&#xff0…

PDshell16逆向PostgreSQL 工程显示字段comment备注

现状&#xff1a;当刚逆向成功的表结构是没有原来表结构中的&#xff0c;comment备注如下 然后pd逆向工程的sql已经返回了这个备注的含义 解决方案&#xff1a; 1、设置显示注释列 tools——Display Preferences…如下 勾选-按照下面得方式勾选这三个 复制这里的VBS脚本&a…

Python数据分析案例37——基于分位数神经网络(QRNN)的汇率预测

案例背景 我导师的研究方向是少有的做"分位数回归"方向&#xff0c;作为研究机器学习深度学习方向的我自然就继承了这个特色&#xff0c;改进出了很多特殊结合方法&#xff0c;我会结合各种机器学习方法和各种分位数回归的方法。 之前写过分位数随机森林&#xff0…

哈希表 -- 刷题(查找算法)

目录 &#x1f4bb;哈希 -- 知识点 &#x1f40d;刷题 &#x1f33c;1&#xff0c;雪花 AC -- vector AC -- 链式前向星 &#x1f33c;2&#xff0c;公式 &#x1f4bb;哈希 -- 知识点 线性表 和 树表&#xff0c;通过比较关键字进行查找 而 散列表&#xff0c;基于…

【MATLAB源码-第119期】基于matlab的GMSK系统1bit差分解调误码率曲线仿真,输出各个节点的波形以及功率谱。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 GMSK&#xff08;高斯最小频移键控&#xff09;是一种数字调制技术&#xff0c;广泛应用于移动通信&#xff0c;例如GSM网络。它是一种连续相位调频制式&#xff0c;通过改变载波的相位来传输数据。GMSK的关键特点是其频谱的…

【接上篇】二、Flask学习之CSS(下篇)

上篇&#xff1a;二、Flask学习之CSS 3.8hover hover是用来美化鼠标悬停的效果的&#xff0c;当鼠标停放在某个区域&#xff0c;就会执行对应的hover操作。可以操作本标签的内容&#xff0c;也可以操作本标签下某一个标签的内容 3.9after <!DOCTYPE html> <html l…

Navicat平替工具,一款免费开源的通用数据库工具

前言 前段时间有小伙伴在群里提问说&#xff1a;因为公司不允许使用破解版的Navicat&#xff0c;有好用的Navicat平替工具推荐吗&#xff1f;今天分享一款免费开源的通用数据库工具&#xff1a;DBeaver。 DBeaver工具介绍 DBeaver是一款免费的跨平台数据库工具&#xff0c;适…

灵活扩展:深入理解MyBatis插件机制

第1章&#xff1a;MyBatis插件的重要性 大家好&#xff0c;我是小黑&#xff0c;咱们今天要聊的是MyBatis插件&#xff0c;MyBatis&#xff0c;大家都不陌生&#xff0c;它是一个ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;让咱们在操作数据库时能更加优雅。但今…

“深入理解 Docker 和 Nacos 的单个部署与集成部署“

目录 引言&#xff1a;Docker Nacos 单个部署1.1 什么是 Docker&#xff1f;Docker 的概念和工作原理Docker 为什么受到广泛应用和认可 1.2 什么是 Nacos&#xff1f;Nacos 的核心功能和特点Nacos 在微服务架构中的作用 1.3 Docker 单个部署 Nacos Docker Nacos 集成部署总结&a…

sfml使用opengl着色器实现2d水面波浪

SFML中使用GLSL着色器来绘制水波。 效果 代码 #include <SFML/Graphics.hpp> #include <iostream>int main() {const int WIDTH = 800;

用C语言实现简单的三子棋游戏

目录 1 -> 模块简介 2 -> test.c 3 -> game.c 4 -> game.h 1 -> 模块简介 test.c:测试游戏逻辑 game.c: 函数的实现 game.h:函数的声明 2 -> test.c #define _CRT_SECURE_NO_WARNINGS 1#include "game.h";void menu() {printf("****…

大模型学习与实践笔记(七)

一、环境配置 1.平台&#xff1a; Ubuntu Anaconda CUDA/CUDNN 8GB nvidia显卡 2.安装 # 构建虚拟环境 conda create --name xtuner0.1.9 python3.10 -y # 拉取 0.1.9 的版本源码 git clone -b v0.1.9 https://github.com/InternLM/xtuner# 从源码安装 XTuner pip insta…

python爬取图片(thumbURL和html文件标签分别爬取)

当查看源代码&#xff0c;发现网址在thumbURL之后时&#xff0c;用此代码: # 当查看源代码&#xff0c;发现网址在thumbURL之后时&#xff0c;用此代码:import requestsheaders {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121…

typing python 类型标注学习笔记

在Python 3.5版本后引入的typing模块为Python的静态类型注解提供了支持。这个模块在增强代码可读性和维护性方面提供了帮助。 目录 简介为什么需要 Type hints typing常用类型typing初级语法typing基础语法默认参数及 Optional联合类型 (Union Type)类型别名 (Type Alias)子类型…

tcp/ip协议2实现的插图,数据结构7 (27 - 章)

(166) 166 二七1 TCP的函数 函tcp_drain,tcp_drop (167) (168)

【AI Superman workshop】AI excel类工具体验

本打卡参与活动&#xff1a;AI Superman workshop&#xff0c;由奇想星球平台与Datawhale联合举办 ChatExcel https://chatexcel.com/ 示例数据 体验内容 1、哪个城市的订单最多&#xff1f; 2、哪个城市的订单最少&#xff1f; 这里能看出订单最少的城市有几十个都为…

CVPR 2023 Hybrid Tutorial: All Things ViTs之DINO attention map

All Things ViTs系列讲座从ViT视觉模型注意力机制出发,本文给出DINO attention map可视化部分阅读学习体会. 课程视频与课件: https://all-things-vits.github.io/atv/ 代码:https://colab.research.google.com/github/all-things-vits/code-samples/blob/main/probing/dino_at…

Windows 11 UEFI引导修复的方法有哪些?

若Windows 11 UEFI 引导加载程序损坏了&#xff0c;您的电脑将无法启动&#xff0c;那么Win11怎么修复UEFI引导&#xff1f;下面我们就来了解一下。 通过自动修复进行UEFI引导修复 1. 将可启动U盘连接到损坏的电脑&#xff0c;进入BIOS设置您的电脑从U盘启动电脑。然后&#x…