【递归、搜索与回溯】搜索

搜索

  • 1.计算布尔二叉树的值
  • 2.求根节点到叶节点数字之和
  • 3. 二叉树剪枝
  • 4.验证二叉搜索树
  • 5.二叉搜索树中第K小的元素
  • 6.二叉树的所有路径

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.计算布尔二叉树的值

题目链接:2331. 计算布尔二叉树的值

题目分析:

在这里插入图片描述

给一颗完整二叉树,孩子节点要么是0要么是2,不存在1个孩子节点。
叶子节点和非叶子节点数字分别代表不同的意思,最后将root根结果bool值返回去。

在这里插入图片描述
算法原理:

解法:递归
宏观角度看待递归
当想知道root根节点bool值时,我要先知道左子树bool值,在知道右子树bool值,然后将左右子数的bool值和root本身信息做整合。当我们要知道左子树和右子树的bool值,你会发现它和大问题是一样的。大问题是解决整棵树的bool值,小问题是解决左子树bool值,右子树bool值。此时把左指针传给dfs,dfs把左子树bool值给你,把右指针传给dfs,dfs把右子树bool值给你。所以dfs非常好设计。
函数头
bool dfs(TreeNode* root)

在这里插入图片描述
函数体也就出来了,先求左子树bool值,在求右子树bool,不要管递归如何执行,只需要知道你给它数据它一定能完成任务。
bool left=dfs(root->left)
bool right=dfs(root->right)
最后在将左子树和右子树bool值和root做整合。

递归出口 到叶子节点就不需要往后递归了,此时返回结果就行了

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.求根节点到叶节点数字之和

题目链接:129. 求根节点到叶节点数字之和

题目分析:

在这里插入图片描述

将根到叶子节点组合的数加起来

在这里插入图片描述

算法原理:

解法:递归
首先我们要找到相同子问题,在树中相同子问题很好找,只需要关注递归到每一层需要干什么事情即可。当它们干的都是一样的事情,这就是相同的子问题。

当递归到5这一层,我发现要拿到和5相连下面的叶子节点的数把它们加起来,然后返回给根节点。当到5这一层

  1. 首先要把12先给我传过来,不然我怎么算出1258,也就是到某一层要把它之前路径上之后给我传过来。然后拿到这个12,先算出125。
  2. 然后把125传给左边
  3. 把125传给右边
  4. 然后把左边值的和与右边值的和相加,然后返回上一层节点

在这5这一层要完成4个任务,同理其他层也是要完成相同的任务。

在这里插入图片描述

1.函数头
首先要有一个返回值,就是传给dfs一个根节点把和它相连的叶子节点的和返回。但是要注意的是,我们除了要传一个根节点,还需要再把递归到该层的上面路径和拿到! 因此函数头是int dfs(root,presum)

2.函数体
1、2、3、4个步骤

3.递归出口
当到叶子节点就可以返回了,但是要注意的是,这个递归出应该在步骤1之后的,因为到叶子节点也要把叶子节点的数加上才向上返回的。

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) ret+=dfs(root->left,presum);
        if(root->right) ret+=dfs(root->right,presum);

        return ret;

    }
};

3. 二叉树剪枝

题目链接:814. 二叉树剪枝

题目分析:

在这里插入图片描述

注意这里的剪枝和前面说的剪枝是不一样的,前面的是那条路不是通往终点的路不能在走了,这里的剪枝真的就是把分枝剪掉!

返回移除了所有 不包含 1 的子树 的原二叉树 的意思是,就是如果子树全是0就把它剪掉。如果子数既有1又有0就不能剪掉。
在这里插入图片描述

算法原理:

通过决策树,抽象出递归的三个核心问题
也就是说通过,完成二叉树剪枝这个任务,完成递归三个核心问题。
像之前先把每个子问题要完成任务先找到,但是有时候某些道题非常麻烦,无法总结出子问题到底干了什么问题,这道题是给一个根节点然后把子树全为0剪掉然后把新根节点返回来。但是有些题特别抽象你根本无法总结出来你让这个递归去完成什么任务,任务太多了。此时我们要有一个能力,通过研究递归的过程来总结出函数头、函数体、递归出口!

在这里插入图片描述

首先这肯定是一个后序遍历,先要确定左右子树是什么情况才能决定是否把这个子树干掉。

当作后序遍历到底最左节点,然后再后序发现它左为空右为空,然后自己本身也是0,说明可以给它剪枝。然后回到上一层但是有一个问题,是不是要修改上一层左子树的指针啊,因此这个递归函数要有一个返回值 TreeeNode*。
在这里插入图片描述
然后如果左右子树都为空,但自己是1,说明不能剪枝
在这里插入图片描述
当我们把根左子树都弄好了,其实整个递归过程就出来。
在这里插入图片描述

  1. 函数头
    TreeNode* dfs(root) 给一个根节点,然后把结果给我
  2. 函数体
    左子树右子树都搞完返回给我一个东西,然后通过返回值在结合我自己看是否需要把自己删除。
    1.左子树、2.右子树、 3.判断
  3. 递归出口
    遇到null结束,就返回null
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)
            return nullptr;
        else 
            return root;
        
    }
};

4.验证二叉搜索树

题目链接:98. 验证二叉搜索树

题目描述:

在这里插入图片描述

算法原理:
这道题主要想说的有三个方面 1. 全局变量的优势 ,2. 回溯 ,3. 剪枝

这里我们利用一个性质,二叉搜索树的中序遍历结果,是一个有序的序列
可能你会想到用一个数组记录中序遍历的结果,然后在遍历一下数组。但是这样空间开销太大了。

我们还是利用这个性质解决这个问题,但是我们可以仅用一个全局变量就来判断这个中序遍历是否是有序的。这个全局变量是在中序遍历中当我遍历到某一个位置它的前序是多少。 并且因为是全局的,并不需要考虑在递归中如何传递,直接拿过来用就可以了!
在这里插入图片描述

当中序遍历到第一个节点后,比较该值和prev的大小,当前这个值比无穷小大说明当前中序遍历是有序的,就更新prev等于这个值。然后向上传,同理依旧是拿这个值和prev做比较等等,知道中序遍历到19发现这个值比prev要小,此时中序遍历就不是有序的,说明30左子树就不一颗二叉搜索树,也说明20右子树不是一颗二叉搜索树,进而说明整棵树就不是一颗二叉搜索树。直接返回即可!

在这里插入图片描述

所以这道题我们可以借助全局变量和中序遍历就可以解决这个问题。
我们有两种策略判断它是否是一个二叉树。
策略一:

  1. 左子树是一颗二叉搜索树,
  2. 当前节点也要符合二叉搜索树的定义
  3. 右子树也是一颗二叉搜索树

此时就颗树就是一颗二叉搜索树!

一个递归99%都有回溯,往上层返回就是回溯!
在这里插入图片描述

当前节点值的范围正好有INT_MIN,如果我们prev=INT_MIN 有可能第一层判断就有问题。因此把prev类型换一下,long 或者 long long

在这里插入图片描述

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)
        {
            prev=root->val;
            cur=true;
        }
        bool right=isValidBST(root->right);

        return left && cur && right;
    }
};

策略二:剪枝

当判断某棵子树不是二叉搜索树,整体就已经不是二叉搜索树了,就没有必要在去其他子树递归了,直接返回结果就行了。加快了我们的搜索过程
在这里插入图片描述
剪枝其实很简单,不要想的那么复杂。

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)
        {
            prev=root->val;
            cur=true;
        }
        //剪枝
        if(cur == false) return false;
        bool right=isValidBST(root->right);

        return left && cur && right;
    }
};

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

题目链接:230. 二叉搜索树中第K小的元素

题目描述:

在这里插入图片描述

算法原理:

有了上面题的基础,这道题就非常简单了,仅需两个全局变量+中序遍历就行了

每次count-1,直到count==0,用ret记录结果。找到最终结果此时其他子树也不用遍历。也可以使用剪枝。

在这里插入图片描述

如果不使用全局遍历,就需要在递归函数中传参,并且还需要考虑参数在递归函数中如何改变。

class Solution {
    int count=0,ret=0;
public:
    int kthSmallest(TreeNode* root, int k) {

        count=k;
        dfs(root);
        return ret;
        // if(root == nullptr) return 0;

        // if(count <= k)
        //     kthSmallest(root->left,k);
        // ++count;
        // if(count == k)
        // {
        //     ret=root->val;
        // }
        // if (count <= k)
        //     kthSmallest(root->right,k);
        // return ret;

    }

    void dfs(TreeNode* root)
    {
        if(root == nullptr || count == 0) return;
        
        dfs(root->left);
        --count;
        if(count == 0) ret=root->val;
        dfs(root->right);
    }
};

6.二叉树的所有路径

题目链接:257. 二叉树的所有路径

题目分析:

在这里插入图片描述

题目很简单,让找根到叶子节点的所有路径。

算法原理:
这道题重点强调的还是

  1. 全局变量
  2. 回溯
  3. 剪枝

其中这道题最重要的是想说回溯 ----> “恢复现场” ,一定纠正思想 因为出现了回溯 才会想到要 恢复现场

只要出现递归必定会有回溯,既然回溯中有恢复现场,那就可以这样说,只要有深度优先遍历就有恢复现场的操作。只不过在简单题恢复现场这个动作并不明显,因为参数在递归过程中就已经自动恢复现场了,但是在一些难的题里面,一旦用到全局变量,我们要手动恢复,这个恢复现场操作就会变成额外重要。

比如这道题我们用两个全局变量,path是把根到叶子节点所经历的路径记录下来,ret是把path到叶子节点后的整条路径记录下来。
在这里插入图片描述

path遇到不是叶子节点就 值 + ->,遇到叶子就把path放到ret数组中。但是此时有一个超级致命的问题,回溯的时候,path要回到上一层,你会发现回溯到上一层path不应该有 4 的,因为path往其他子树传仅需要传 1->2-> 就行了!那此时就应该 恢复现场把全局变量恢复下去之前的样子。此时还有一个问题如果把path设计成全局变量,回溯时path要不停pop,从叶子节点回溯还好说,但是从非叶子节点回溯要pop两次,挺麻烦的!

在这里插入图片描述

这道题用全局变量path记录路径比较麻烦,仅是这道题全局path不好用,但是比较麻烦的题全局path好用,这里只是为了说明,有恢复现场这个操作的。

这道题我们函数头这样设计 void dfs(root,path),把path当成参数传给函数,你会发现恢复现场特别容易,函数特性就已经帮我们恢复现场了。因为每次函数都会重新创建一个path,回溯到上一层用的还是上一层自己的path。就不用自己手动恢复了。

在这里插入图片描述

总结一下:全局变量 不好手动 恢复现场,那就 参数 自动 恢复现场

函数头,函数体都差不多了,接下来就是递归出口了,当root == nullptr 返回。但是这里递归还要进去才返回。此时我们可以剪枝,当非空进去,空就不要进!

在这里插入图片描述

class Solution {
    vector<string> ret;
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        string path;
        dfs(root,path);
        return ret;
    }

    void dfs(TreeNode* root,string path)
    {
        //if(root == nullptr) return;

        path+=to_string(root->val);
        if(root->left == nullptr && root->right == nullptr)
        {
            ret.push_back(path);
            return;
        }

        path+="->";
        // dfs(root->left,path);
        // dfs(root->right,path);

        //回溯+剪枝 if就是剪枝
        if(root->left) dfs(root->left,path);

        //中间如果有就是回溯

        if(root->right) dfs(root ->right,path);
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入ES6:解锁 JavaScript 类与继承的高级玩法

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; ES5、ES6介绍 文章目录 &#x1f4af;Class&#x1f35f;1 类的由来&#x1f35f;2 co…

【文献阅读】LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

目录 1. motivation2. overall3. model3.1 low rank parametrized update matrices3.2 applying lora to transformer 4. limitation5. experiment6. 代码参考文献 1. motivation 常规的adaptation需要的微调成本过大现有方法的不足&#xff1a; Adapter Layers Introduce Inf…

视创云展元宇宙虚拟展厅:开启无限可能的沉浸式体验

随着科技的飞速发展&#xff0c;元宇宙虚拟展厅已逐步成为展览行业的新宠。视创云展元宇宙虚拟展厅以其独特的魅力&#xff0c;将参观者从传统展览场所的束缚中解放出来&#xff0c;为他们呈现了一个更为广阔、更为丰富的虚拟世界。通过数字虚拟展厅这一载体&#xff0c;参观者…

如何掌握 Java 正则表达式 的基本语法及在 Java 中的应用

正则表达式是一种用于匹配字符串的模式&#xff0c;在许多编程语言中广泛使用。Java 正则表达式提供了强大的文本处理能力&#xff0c;能够对字符串进行查找、替换、分割等操作。 一、正则表达式的基本语法 正则表达式由普通字符和特殊字符组成。普通字符包括字母、数字和标点…

【Android】主界面设置-封装

在bulid文件写网址 implementation("io.github.youth5201314:banner:2.2.1") 添加主界面图片 些内容 在界面有图片&#xff0c;相同的属性封装起来 在values新建 先写风格&#xff0c;&#xff0c;再写代码 先写好这几项&#xff0c;宽高比例位置 将相同的属性…

软硬件集成项目,这个项目管理软件做的成本预算管理深得我心

最近&#xff0c;我负责了一个中大型的软硬件集成的项目&#xff0c;是对某单位的车间进行智能化改造&#xff0c;以提高生产效率&#xff0c;要确保设备运行的稳定性和安全性。项目会涉及到大量的硬件采购、安装以及多个软件的开发、集成&#xff0c;所以在实施过程中遇到了多…

【Python】实现极致:克服PyInstaller打包挑战,解决libpython3.10.so.1.0库丢失难题

【Python】实现极致&#xff1a;克服PyInstaller打包挑战&#xff0c;解决libpython3.10.so.1.0库丢失难题 大家好 我是寸铁&#x1f44a; 总结了一篇【Python】实现极致&#xff1a;克服PyInstaller打包挑战&#xff0c;解决libpython3.10.so.1.0库丢失难题✨ 喜欢的小伙伴可以…

微软必应地图的三维实景功能

偶然看到微软必应地图的三维实景功能&#xff0c;由于比较感兴趣这方面的技术&#xff0c;所以试用了一下,感觉总体来说技术上比咱们自己的技术和设计要好很多。比如这个工具栏就设计的很简洁&#xff0c;人性化&#xff1a; 而且实景地图的范围也非常大&#xff0c;建立这么大…

Windows系统中不同Java版本共存

Windows系统中不同Java版本共存的方法 在Windows系统中&#xff0c;有时我们需要同时运行多个Java应用&#xff0c;而这些应用可能依赖于不同版本的Java Development Kit (JDK) 或 Java Runtime Environment (JRE)。为了实现这种需求&#xff0c;我们需要在Windows中配置多个J…

自养号测评防关联的关键点解析, 确保店铺权重和买家账号的安全稳定

现在很多大卖都是自己管理几百个账号&#xff0c;交给服务商不是特别靠谱。你不知道服务商账号质量怎么样&#xff0c;账号一天下了多少你也不清楚&#xff0c;如果下了很多单万一封号被关联了怎么办&#xff0c;你也不知道服务商用什么卡给你下单&#xff0c;用一些低汇率和黑…

【Python Cookbook】S02E04 文本模式的匹配和查找 match()、search()、findall() 以及 捕获组和 + 的含义

目录 问题解决方案讨论 问题 本文讨论一些按照特定的文本模式进行的查找和匹配。 解决方案 如果想要匹配的只是简单文字&#xff0c;通常我们使用一些内置的基本字符串方法即可&#xff0c;如&#xff1a;str.find()&#xff0c;str.startwith()&#xff0c;str.endswith() …

qmt量化交易策略小白学习笔记第17期【qmt编程之获取对应周期的北向南向数据--方式1:内置python】

qmt编程之获取对应周期的北向南向数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 感谢关注&#xff0c;咨询免费开通量化回测与获取实盘权限&#xff0c;欢迎和博主联系&#xff01; 获取…

Qwen2本地部署的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

重塑状态管理的艺术:Vue3中Pinia的魔法之旅内包含简易购物车案例

前言 在Vue.js的世界里&#xff0c;每一次更新都是一次进化&#xff0c;Vue3携带着更强大的性能与灵活性翩然而至。而在这场技术盛宴中&#xff0c;Pinia以一种优雅而革命性的方式&#xff0c;重新定义了状态管理的体验。如果说Vuex是Vue2时代的王者&#xff0c;那么Pinia无疑…

024、工具_慢查

1)发送命令 2)命令排队 3)命令执行 4)返回结果 需要注意,慢查询只统计步骤3)的时间,所以没有慢查询并不代表客 户端没有超时问题。 参数配置 slowlog-log-slower-than 单位是微秒(1秒=1000毫秒=1000000微秒),默认值是10000 lowlog-log-slower-than=0会记录所有的命…

Polar Web【简单】upload1

Polar Web【简单】upload1 Contents Polar Web【简单】upload1思路EXP运行&总结 思路 本题思路同之前两篇文中的文件上传题目性质相同&#xff0c;这里再次记录&#xff0c;旨在改良之前的脚本编写方式 —— 脚本运行后变为可交互的命令行形式。 打开环境&#xff0c;见要求…

算法课程笔记——可撤销并查集

算法课程笔记——可撤销并查集 Gv

网络协议三

数据中心 一、DNS 现在网站的数目非常多&#xff0c;常用的网站就有二三十个&#xff0c;如果全部用 IP 地址进行访问&#xff0c;恐怕很难记住 根 DNS 服务器 &#xff1a;返回顶级域 DNS 服务器的 IP 地址 顶级域 DNS 服务器&#xff1a;返回权威 DNS 服务器的 IP 地址 …

k8s:实现一个pod两个容器

# 制作两个容器的镜像 通过以下Dockerfile创建一个镜像 cd /chz/install/docker vim Dockerfile <<<< 内容如下&#xff1a; FROM centosRUN sed -i -e "s|mirrorlist|#mirrorlist|g" /etc/yum.repos.d/CentOS-* RUN sed -i -e "s|#baseurlhttp:/…

vue2的form利用插槽修改错误提示UI

1. 需求 很多时候我们使用el-form想修改下错误提示的UI&#xff0c;比如table中使用form校验这类场景下错误提示的UI调整就非常重要。 2. 了解文档 Form-Item Scoped Slot name说明error自定义表单校验信息的显示方式&#xff0c;参数为 { error } 3.实际使用 html里使用…