关于递归和回溯的思考

完整代码: 力扣112路径总和

class Solution {
private:bool traversal(TreeNode* cur, int count) {if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回if (cur->left) { // 左count -= cur->left->val; // 递归,处理节点;if (traversal(cur->left, count)) return true;count += cur->left->val; // 回溯,撤销处理结果}if (cur->right) { // 右count -= cur->right->val; // 递归,处理节点;if (traversal(cur->right, count)) return true;count += cur->right->val; // 回溯,撤销处理结果}return false;}public:bool hasPathSum(TreeNode* root, int sum) {if (root == NULL) return false;return traversal(root, sum - root->val);}
};

写法一

if (cur->left) { // 左count -= cur->left->val; // 递归,处理节点;if (traversal(cur->left, count)) return true;count += cur->left->val; // 回溯,撤销处理结果}

写法一*

if (cur->left) { // 左if (traversal(cur->left, count - cur->left->val)) return true;}

写法二

return hasPathSum(root->left, sum - root->val)

两种写法是否等价? 为什么

这两段代码在功能上是等价的,但是它们的实现方式和表达的意图有所不同。

第一段代码是使用了回溯法的思想:

  1. 首先,它检查当前节点的左子节点是否存在。
  2. 如果存在,它先减去左子节点的值,然后递归地调用traversal函数来处理左子节点。
  3. 如果递归调用返回true,则整个函数立即返回true
  4. 如果递归调用返回false,则进行回溯,即撤销之前的操作,将减去的值加回来。

第二段代码是使用了递归的思想:

  • 它直接递归调用hasPathSum函数来处理当前节点的左子节点,同时更新目标和为sum - root->val
  • 这种方式相当于在每一层递归中传递一个更新后的目标和,而不是在同一层中修改和回溯目标和的值。

两种方式都是在尝试找到一条从当前节点到叶子节点的路径,使得路径上节点值的总和等于给定的目标和。在逻辑上,它们是等价的,只是实现方式和表达的思想有所不同。第一种方式更直观地体现了回溯的过程,而第二种方式则更简洁地利用了递归的特性。在实际应用中,可以根据具体情况和个人偏好选择使用哪一种方式。

问题: 为什么第一种方式需要回溯

第一种方式需要回溯是因为它在同一层递归中直接修改了共享的状态(例如目标和 count),而不是传递新的状态给下一层递归。这意味着在当前层递归中对状态的修改会影响到其他递归分支,所以在探索完一个分支后需要撤销这些修改,以便于正确地探索其他分支。这个过程就是回溯。

具体来说,在第一种方式中:

  1. 当进入一个新的递归分支时(例如探索左子树),它会先减去当前节点的值 cur->left->val,更新目标和 count
  2. 然后它递归地探索这个分支。如果这个分支最终满足条件,递归调用会返回 true,整个搜索过程会提前结束。
  3. 如果这个分支不满足条件,递归调用会返回 false,这时需要撤销之前对 count 的修改(即加回 cur->left->val),以便于 count 的值对于探索其他分支(例如右子树)是正确的。

这种需要显式回溯的方式给状态管理带来了额外的复杂性,但它也提供了更大的灵活性,因为它允许你在同一层递归中多次修改和恢复状态。

问题: 为什么第二种方式中不用回溯

第二种方式中不需要回溯,因为它在每次递归调用时都传递了一个新的目标和,而不是直接修改原有的目标和。

在第二种方式中,每一次递归调用 hasPathSum(root->left, sum - root->val) 都是创建了一个新的函数调用栈,这个新的调用栈有自己的局部变量 sum。在这个新的调用栈中,sum 的值是上一层调用栈中的 sum - root->val,这个值在这个新的调用栈中是固定的,不会因为其他递归调用的结果而改变。因此,当这个递归调用返回后,上一层调用栈中的 sum 的值仍然是未修改的,所以不需要进行回溯操作。

换句话说,每一层递归都有自己的变量环境,这些变量环境是相互独立的,所以不需要显式地进行回溯操作来恢复之前的状态。这是递归方法的一个特点,它通过函数调用栈来隐式地实现状态的保存和恢复。

写法一和写法一*完全相同

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

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

相关文章

[StartingPoint][Tier1]Funnel

Task 1 How many TCP ports are open? (打开了多少个 TCP 端口?) # nmap -sS -T4 10.129.224.226 --min-rate 1000 2 Task 2 What is the name of the directory that is available on the FTP server? (FTP 服务器上可用的目录名称是什么?) $ n…

数据库系统概论(超详解!!!)第三节 关系数据库标准语言SQL(Ⅵ)

1.空值的处理 空值就是“不知道”或“不存在”或“无意义”的值。 一般有以下几种情况: 该属性应该有一个值,但目前不知道它的具体值 ;该属性不应该有值 ;由于某种原因不便于填写。 1.空值的产生 空值是一个很特殊的值&#x…

云仓酒庄旗下雷盛红酒入驻香港星怡SingLa餐厅共绘美食美酒新篇章

近日,云仓酒庄旗下品牌雷盛红酒正式入驻香港餐厅星怡SingLa,这一跨界合作不仅为香港市民和游客带来了全新的味蕾享受,也标志着美食与美酒文化的很好结合,共同绘就了一幅精彩绝伦的美食美酒新篇章。 云仓酒庄一直以来都致力于为消费…

Rust 程序设计语言学习——枚举模式匹配

枚举(enumerations),也被称作 enums。match 允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行相应代码。 1 枚举的定义 假设我们要跨省出行,有多种交通工具供选择。常用的交通工具有飞机、火车、汽车和轮…

备战蓝桥杯Day37 - 真题 - 特殊日期

一、题目描述 思路: 1、统计2000年到2000000年的日期,肯定是需要遍历 2、闰年的2月是29天,非闰年的2月是28天。我们需要判断这一年是否是闰年。 1、3、5、7、8、10、12月是31天,4、6、9、11月是30天。 3、年份yy是月份mm的倍数…

【Entity Framework】EF配置文件设置详解

【Entity Framework】EF配置文件设置详解 文章目录 【Entity Framework】EF配置文件设置详解一、概述二、实体框架配置部分三、连接字符串四、EF数据库提供程序五、EF侦听器六、将数据库操作记录到文件中七、Code First默认连接工厂八、数据库初始值设定项 一、概述 EF实体框架…

OKR应用层级与试点部门选择:管理层与员工层的应用探讨

OKR(Objectives and Key Results)作为一种高效的目标管理工具,其应用层级的选择对于企业的实施效果至关重要。在管理层和员工层之间,并没有绝对的先后顺序,而是需要根据企业的具体情况和需求进行灵活应用。同时&#x…

CODEFORCES --- 630A. Again Twenty Five!

630A. Again Twenty Five! 人力资源经理又失望了。最后一名应聘者和之前的 24 名应聘者一样,都没有通过面试。"我应该给这样一个艰巨的任务吗?- 人力资源经理想。“只要把数字 5 提高到 n 的幂,然后得到数字的最后两位就可以了。是的&a…

stata 数据匹配

横向匹配(增加变量)——merge merge 1:1 id using otherfile.dta匹配城市 merge m:1 city using "E:\基点.dta",nogen匹配上市公司 merge m:1 stkcd time using "E:\基点.dta",nogen匹配类型: 1:1: 1配1 m:1:多配1 …

QEMU介绍

原文位置:https://github.com/qemu/qemu 原文 QEMU is a generic and open source machine & userspace emulator and virtualizer. QEMU is capable of emulating a complete machine in software without any need for hardware virtualization support. B…

前端八股文面试题——webpack工程化

前端工程化面试题 webpack有哪些常见的loader? 你用过哪些loader?webpack 有哪些常见的Plugin? 你用过哪些Plugin?说说Loader 和Plugin 的区别作用上结构上 webpack 构建流程简单说一下使用webpack开发时,使用过哪些可以提高效率的插件?如何优化webp…

python买铅笔 2024年3月青少年电子学会等级考试 中小学生python编程等级考试一级真题答案解析

目录 python买铅笔 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python买铅笔 2024年3月 python编程等级考试级编程题 一、题目要求 1、编…

习题3-4 统计学生成绩

本题要求编写程序读入N个学生的百分制成绩,统计五分制成绩的分布。百分制成绩到五分制成绩的转换规则: 大于等于90分为A;小于90且大于等于80为B;小于80且大于等于70为C;小于70且大于等于60为D;小于60为E。…

【电路笔记】-逻辑非门

逻辑非门 文章目录 逻辑非门1、概述2、晶体管逻辑非门3、六角施密特反相器逻辑非门是所有逻辑门中最基本的,通常称为反相缓冲器或简称为反相器。 1、概述 反相非门是单输入器件,其输出电平通常为逻辑电平“1”,当其单个输入为逻辑电平“1”时,输出电平变为“低”至逻辑电平…

通用爬虫的概念简述

一、🌈什么是通用爬虫 通用爬虫(General Purpose Web Crawler或Scalable Web Crawler)是一种网络爬虫,其设计目标是对整个互联网或尽可能广泛的网络空间进行数据抓取。通用爬虫主要用于搜索引擎构建其庞大的网页索引数据库&#…

FineReport安装后,启动报错:get tomcat thread pool info error

报错信息如下,一堆堆的错误: 1、get tomcat thread pool info error 2、lineofficial plugin is not istalled 3、line...not installed 4、lark... 5、dingtalk ... 最后解决方案: 1、安装java jdk1.8 2、安装了tomcat 8 安装版 3、…

MBTI测试深度解析:从性格类型看职业选择与发展方向!(包含开源免费的API接口)

MBTI简介 迈尔斯-布里格斯类型指标(Myers–Briggs Type Indicator,MBTI)是由美国作家伊莎贝尔布里格斯迈尔斯和她的母亲凯瑟琳库克布里格斯共同制定的一种人格类型理论模型。 该指标以瑞士心理学家卡尔荣格划分的8种心理类型为基础&#xff…

使用LIMIT进行分页

SELECT employee_id, first_name, salary FROM employees LIMIT 0, 5; 0为偏移量, 5为条目数 每页pageSize条记录,显示第page页 LIMIT (page - 1) * pageSize, pageSize; # 或者 LIMIT pageSize OFFSET (page - 1) * pageSize;

备战蓝桥杯---递归与DFS刷题2

1. 数据范围允许直接暴力把所有组合都写一遍,我们用Pair来存,在sort中分式比较只要把自己的分子与对方的分母乘比较即可,下面介绍一下st树的写法,具体原理就不说了,它是先[0/1,1/1]然后取分子分母的平均化成两个区间&a…

web学习笔记(五十三)身份认证

目录 1.Web 开发模式 1.1 服务端渲染的 Web 开发模式 1.2 服务端渲染的优缺点 1.3 前后端分离的 Web 开发模式 1.4 如何选择 Web 开发模式 2. 身份认证 2.1 Session 认证机制 3. 在 Express 中使用 Session 认证 3.1 安装express-session 中间件 3.2 配置 express-ses…