[Algorithm][递归][搜索][回溯]总体理解+ 算法总结 (极其干货哦~)

news2025/4/8 22:06:39

目录

  • 0.观前必读
  • 1.递归
    • 1.什么是递归?
    • 2.为什么会用到递归?
    • 3.如何理解递归?
    • 4.如何写好一个递归?
  • 2.概念乱斗:P
    • 1.深度优先遍历 vs 深度优先搜索 && 宽度优先遍历 vs 宽度优先搜索
    • 2.搜索 vs 暴搜
    • 3.拓展搜索问题
  • 3.回溯与剪枝
  • 4.总结
    • 1.递归 vs 深搜
    • 2.迭代 vs 递归
    • 3.前序遍历 vs 后序遍历
  • 5.经验之谈
    • 1.全局变量的优势
    • 2.剪枝
    • 3.回溯
  • 6.记忆化搜索 VS 动态规划
    • 1.思路是什么?
    • 2.本质理解
    • 3.问题思考 && 总结


0.观前必读

  • 本篇为总结篇,建议看了后面的博文后,再来看本篇,会有不一样的感受哦~

1.递归

1.什么是递归?

  • 函数自己调用自己

2.为什么会用到递归?

  • 本质:解决一个问题时,出现相同的子问题
    • 主问题 -> 相同的子问题
    • 子问题 -> 相同的子问题
    • ……
      请添加图片描述

3.如何理解递归?

  • 分为三个阶段
    1. 递归展开的细节图
    2. 二叉树中的题目
    3. 宏观看待递归的过程
      • 不要在意递归的细节展开图
      • 把递归的函数当成一个黑盒
      • 相信这个黑盒一定能完成这个任务
  • 用以下例子形象感受
// 二叉树的后序遍历
void DFS(TreeNode* root)
{
	// 出口
	if(root == nullptr)
	{
		return;
	}

	DFS(root->left);  // 相信这个函数一定可以做到,遍历左子树
	DFS(root->right); // 相信这个函数一定可以做到,遍历右子树
	cout << root->val;
}

void Merge(vector<int>& nums, int left, int right)
{
	// 出口
	if(left >= right)
	{
		return;
	}
	
	int mid = left + (right - left) / 2;
	Merge(nums, left, mid);      // 相信这个函数一定可以做到,排序左区间
	Merge(nums, mid + 1, right); // 相信这个函数一定可以做到,排序右区间

	// Merge...
}

4.如何写好一个递归?

  1. 先写到相同的子问题
    • 这将决定:函数头的设计
  2. 只关心某一个子问题是如何解决的
    • 这将决定:函数体的书写
  3. 注意一下递归函数的出口

2.概念乱斗:P

1.深度优先遍历 vs 深度优先搜索 && 宽度优先遍历 vs 宽度优先搜索

  • 搜索与遍历相比,只多了访问结点值这一步,所以可以这样认为
    • 深度优先遍历 == 深度优先搜索
    • 宽度优先遍历 == 宽度优先搜索
  • 遍历是形式,目的是搜索
    请添加图片描述

2.搜索 vs 暴搜

  • 搜索:暴力枚举一遍所有的情况
  • 搜索 == 暴搜
    • BFS
    • DFS

3.拓展搜索问题

  • 全排列 <- 决策树
    请添加图片描述

3.回溯与剪枝

  • 回溯本质:深搜(DFS)
    • 回退就是回溯,不用区分那么细
  • 剪枝:在一个结点有两种选择,但是已经明确知道其中一种选择不是想要的结果,可以剪掉(去掉)这种结果/情况
    • 在树中,就是形象的剪掉了一个叶子或者某一个结点的子树
      请添加图片描述

4.总结

1.递归 vs 深搜

  • 递归的展开图,其实就是对一棵树做了一次深度优先遍历(DFS)
    • 此处的树不局限于二叉树,多叉树也可以
      请添加图片描述

2.迭代 vs 递归

  • 本质:都是解决重复的子问题

  • 迭代可以改递归,递归也可以改迭代

    • 如果树的递归想改成迭代,需要借助来存储当前信息以帮助解决问题
      • 因为树的递归时,当执行完左子树之后,整个函数是还没有被执行完的,还要进行后续操作,并且递归展开执行右子树的内容
      • 所以当递归改成迭代时,就需要借助,来保存此时的信息,在完成"左子树"逻辑后,再进行后续操作
  • 什么时候迭代舒服,什么时候递归舒服?

    • 递归展开图比较麻烦的时候,递归舒服
    • 递归展开图只有一个分支时,迭代舒服
      请添加图片描述
  • 用下面的代码感受下:如何遍历一个数组?

void ContainDuplicate(vector<int>& nums)
{
	// 迭代
	for(int i = 0; i < nums.size(); i++)
	{
		cout << nums[i] << " ";
	}

	// 递归
	DFS(nums, 0);
}

void DFS(vector<int>& nums, size_t i)
{
	if(i == nums.size())
	{
		return;
	}

	cout << nums[i] << " ";
	DFS(nums, i + 1);
}

3.前序遍历 vs 后序遍历

  • 补充:中序遍历是只在二叉树中才有的,多叉树是不适用的
  • 前序遍历和后序遍历都是DFS,只是访问结点的时机不一样
  • 以上面的遍历数组为例,可以看出前序遍历和后序遍历只是两行代码交换了一下顺序而已,本质就是访问结点的时机变了
void ContainDuplicate(vector<int>& nums)
{
	// 迭代
	for(int i = 0; i < nums.size(); i++)
	{
		cout << nums[i] << " ";
	}

	// 递归
	DFS(nums, 0);
}

// 前序遍历
void DFS1(vector<int>& nums, size_t i)
{
	if(i == nums.size())
	{
		return;
	}

	cout << nums[i] << " ";
	DFS(nums, i + 1);
}

// 后序遍历
void DFS1(vector<int>& nums, size_t i)
{
	if(i == nums.size())
	{
		return;
	}

	DFS(nums, i + 1);
	cout << nums[i] << " ";
}
  • 后序遍历按照左⼦树、右⼦树、根节点的顺序遍历⼆叉树的所有节点,通常⽤于⽗节点的状态依赖于⼦节点状态的题⽬

5.经验之谈

1.全局变量的优势

  • 在解决递归问题时,全局变量有时会大大的简化递归模型的设计和问题的抽象
  • 比如二叉树的中序遍历,全局变量就可以用来保存上一个访问的结点的状态值
    • 此时,在中序遍历中,想把状态在每层递归中传递,就不会像前序遍历那样直接当成参数设计那样来的方便

2.剪枝

  • 在判断此时条件已经不复合要求时,此时可以果断舍去后面的递归过程
    • 因为此时已经知道结果,再往后继续递归展开判断也是没有意义的了
  • 剪枝在递归中,可以加快搜索过程
    • 具体感受可见「验证二叉搜索树」

3.回溯

  • 理解顺序:回溯 -> 恢复现场
  • 回溯:恢复现场,通常有两种做法
    • 全局变量:一般是数组时使用比较好
    • 函数传参:一般是单个变量时使用比较好
      • 此时的回溯,是编译器/代码代为做了回溯,开销较小

6.记忆化搜索 VS 动态规划

1.思路是什么?

  • 记忆化搜索:带备忘录的递归:P
  • 动态规划一般思路
    • 确定状态表示 -> dp[i]的含义
    • 推导状态转移方程
    • 初始化
    • 确定填表顺序
    • 确定返回值

2.本质理解

  • 大部分情况下,记忆化搜索的代码是可以改成动态规划代码的
  • 以斐波那契数列举例
    • 确定状态表示dp[i] -> 第i个斐波那契数
      • DFS()的含义
    • 推导状态转移方程dp[i] = dp[i - 1] + dp[i - 2]
      • DFS()的函数体
    • 初始化dp[0] = 0, dp[1] = 1
      • DFS()的递归出口
    • 确定填表顺序
      • 填写备忘录的顺序
    • 确定返回值:主函数是如何调用DFS()
  • 动态规划和记忆化搜索本质
    • 暴力解法 -> 暴搜
    • 对递归解法的优化:把已经计算过的值,存起来
  • 《算法导论》中,记忆化搜索和常规的动态规划都被归为动态规划的范畴,区别为:
    • 记忆化搜索 -> 递归
    • 常规的动态规划 -> 递推(循环)

3.问题思考 && 总结

  • 所有的递归(暴搜,深搜),都可以改成记忆化搜索吗?

    • 不能
    • 只有在递归的过程中,出现了大量完全相同的问题时,才能用记忆化搜索的方式优化
  • 带备忘录的递归 VS 带备忘录的动态规划 VS 记忆化搜索

    • 都是一回事:P
  • 自顶向上 VS 自底向上

    • 区别:解决问题时,不同的思考方式
    • 自顶向下 -> 递归
    • 自底向上 -> 动态规划
      请添加图片描述
  • 大多数情况下,暴搜 -> 记忆化搜索 -> 动态规划,这条优化代码的思考线路是没问题的

    • 但是并不绝对,有时候直接思考动态规划比思考暴搜来的方便
    • 因人而异,因题而异
    • 总结:这条思考线路为我们确定状态表示,提供一个方向

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

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

相关文章

PostgreSQL建表语句 INT, INT2, INT4, INT8 分别对应Java,Go, Python什么数据类型?

&#x1f42f; PostgreSQL建表语句 INT, INT2, INT4, INT8 分别对应Java&#xff0c;Go, Python什么数据类型 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题…

selenium进行xhs图片爬虫:06xhs一个博主的全部文章图片爬取

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…

计算机毕业设计源码 | 基于SpringBoot的线上教学系统 答疑辅导网站(附源码)

1&#xff0c;项目介绍 1.1 项目背景 网络问答平台经历了多年的发展&#xff0c;目前处于一个日益成熟的状态。最早的网络问答平台是知乎&#xff0c;知乎的创立者认为有许多信息在互联网上没有被记录和共享&#xff0c;于是他们决定创造一个平台&#xff0c;能够让更多人可以…

Spring的IOC和AOP机制?

我们是在使用Spring框架的过程中&#xff0c;其实就是为了使用IOC&#xff0c;依赖注入&#xff0c;和AOP&#xff0c;面向切面编程&#xff0c;这两个是Spring的灵魂。 主要用到的设计模式有工厂模式和代理模式。 IOC就是典型的工厂模式&#xff0c;通过sessionfactory去注入…

CVE-2015-1635(MS15-034 ) 远程代码执行漏洞

1.打补丁 https://docs.microsoft.com/zh-cn/security-updates/Securitybulletins/2015/ms15-034 2.临时禁用IIS内核缓存

大型语言模型自我进化综述

24年4月来自北大的论文“A Survey on Self-Evolution of Large Language Models”。 大语言模型&#xff08;LLM&#xff09;在各个领域和智体应用中取得了显着的进步。 然而&#xff0c;目前从人类或外部模型监督中学习的LLM成本高昂&#xff0c;并且随着任务复杂性和多样性的…

【STM32 |外部中断】中断系统、EXTI外部中断

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 丠丠64-CSDN博客&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起…

一图看懂git merge和git rebase的区别!!

一图看懂git merge和git rebase的区别&#xff01;&#xff01; Git 是一个非常流行的版本控制系统&#xff0c;它帮助开发者管理代码的不同版本。在 Git 中&#xff0c;merge 和 rebase 是两种常用的将不同分支的更改合并到一起的方法&#xff0c;但它们在处理方式和结果上有…

第四届微调——炼丹

学习地址&#xff1a;Tutorial/xtuner/README.md at main InternLM/Tutorial GitHub 笔记 微调是一种在已有的预训练模型基础上&#xff0c;通过使用新的数据对模型进行进一步优化和调整的技术手段。它的目的是使模型能够更好地适应特定的应用场景和任务需求&#xff0c;进一…

融入新科技的SLM27211系列 120V, 3A/4.5A高低边高频门极驱动器兼容UCC27284,MAX15013A

SLM27211是高低边高频门极驱动器&#xff0c;集成了120V的自举二极管&#xff0c;支持高频大电流的输出&#xff0c;可在8V~17V的宽电压范围内驱动MOSFET&#xff0c;独立的高、低边驱动以方便控制&#xff0c;可用于半桥、全桥、双管正激和有源钳位正激等拓。有极好的开通、关…

26 Debian如何配置SSH密钥对验证(支持无密码安全登录)

作者&#xff1a;网络傅老师 特别提示&#xff1a;未经作者允许&#xff0c;不得转载任何内容。违者必究&#xff01; Debian如何配置SSH密钥对验证 《傅老师Debian小知识库系列之26》——原创 前言 傅老师Debian小知识库特点&#xff1a; 1、最小化拆解Debian实用技能&…

【什么是范数】L1、L2范数介绍

一、 什么是范数 范数&#xff08;Norm&#xff09;是数学中用于衡量向量大小(或距离)的一种概念。在几何和代数中&#xff0c;范数可以提供一个向量长度或大小的量度。更正式地说&#xff0c;范数是一个函数&#xff0c;它将向量的集合映射到非负实数&#xff0c;满足以下性质…

【WP网站开发新的订单查询页面,根据付费单号未登录用户可查询资源下载地址】RiProV2主题美化WordPress美化订单查询页面,二次开发DIY的功能页面

【WP网站开发新的订单查询页面,根据付费单号未登录用户可查询资源下载地址】RiProV2主题美化WordPress美化订单查询页面,二次开发DIY的功能页面 背景: 很多资源站是使用,WordPress搭建的PHP网站,一般会额外使用一个主题,比如RiProV2,Ritheme,日主题;本文以 RiProV2主…

物联网实战--平台篇之(七)应用界面设计

目录 一、米家APP分析 二、应用展示 三、应用列表 四、新建应用 五、重命名应用 本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.…

jar包启动报错no main manifest attribute

我是这里加了这个跳过&#xff0c;把这个注释掉即可。

SSL证书助力工业和信息化领域数据安全,确保传输数据的保密性、完整性

工业和信息化领域数据包括工业数据、电信数据和无线电数据等&#xff0c;是国家重要基础性战略资源&#xff0c;随着工业领域数字化、网络化、智能化加速提质升级&#xff0c;数据泄露、勒索攻击等网络风险日益增加&#xff0c;由此加强工业和信息化领域数据安全管理&#xff0…

RiProV2主题美化【支付页弹窗增加价格提示语】Ritheme主题美化RiProV2-网站WordPress美化二开

背景: 楼主的网站是用WordPress搭建的,并使用了正版主题RiProV2,但RiProV2在支付弹窗页没有价格,只在文章详情页会展示价格。本文就是美化这个支付弹窗,在支付弹窗页把价格字段加上,如下图所示: 美化前: 美化后 美化步骤: (1)定位到文件:/www/wwwroot/www.uu2i…

【牛客】SQL211 获取当前薪水第二多的员工的emp_no以及其对应的薪水salary

1、描述 有一个薪水表salaries简况如下&#xff1a; 请你获取薪水第二多的员工的emp_no以及其对应的薪水salary&#xff0c; 若有多个员工的薪水为第二多的薪水&#xff0c;则将对应的员工的emp_no和salary全部输出&#xff0c;并按emp_no升序排序。 2、题目建表 drop table …

考研数学|24像张宇那样的题?李林880和李永乐660不够用了?

以前的卷子就不说了&#xff0c;就说说最近的24年的考研数学题 24年考研数学真题评价&#xff1a; 首先数学二在计算量上超过了数学三&#xff0c;尤其是在高等数学的选择题部分&#xff0c;这使得数学二的难度可能略高于数学三&#xff0c;尽管两者之间并没有本质的差异。与…

附录2 创建flask镜像

目录 1 python镜像 2 安装flask 3 把项目文件扔进去 3.1 创建git仓库 3.2 上传文件 3.3 获取git链接 3.4 在容器中git clone 4 启动flask服务 5 将容器保存为镜像 6 映射端口运行镜像 7 遇到的问题 8 Dockerfile创建镜像 1 python镜像 首先找一下fla…