【C++】递归,搜索与回溯算法入门介绍和专题一讲解

news/2024/6/17 19:05:06 标签: 算法, c++, 深度优先, dfs

在这里插入图片描述
在这里插入图片描述

个人主页:🍝在肯德基吃麻辣烫
我的gitee:C++仓库
个人专栏:C++专栏

前言

从本文开始进入递归,搜索与回溯算法专题讲解。

文章目录

  • 前言
  • 一、名词解释
    • 1、什么是递归?
    • 2、为什么会用到递归?
    • 3、如何理解递归?
    • 4、如何写好递归?
  • 二、搜索vs深度优先遍历vs深度优先搜索vs宽度优先遍历vs宽度优先搜索vs暴搜
    • 1、深度优先遍历vs深度优先搜索
    • 2、宽度优先遍历vs宽度优先搜索
    • 3、关系图
    • 4. 搜索问题的拓展
  • 三、回溯与剪枝
  • 四、专题一
    • 1. 汉诺塔问题
    • 算法分析
    • 代码编写
  • 总结



一、名词解释

1、什么是递归?

递归就是函数自己调用自己。


2、为什么会用到递归?

递归的本质是:

主问题:—>相同的子问题
子问题:—>相同的子问题

3、如何理解递归?

通过:

  • 1)通过递归的细节展开图(前期可以,过了前期一定不能再用了)
  • 2)通过二叉树中的题目
  • 3)宏观看待递归问题(重要)

越往后学越发现,如果只抓住递归的细节展开图,你会发现你根本就学不好递归这个东西,递归的细节展开图只是为了辅助你读过新手期,如果你后面还在用它,那么你往往是学不好递归的。

那么:如何理解宏观看待递归问题这个点呢?

可以分为几个部分:

    • 1)不要再在意递归的细节展开图
    • 2)把递归的函数当成一个黑盒子
    • 3)相信这个黑盒子一定能完成这个任务

4、如何写好递归?

写好一个递归也分为三点:

  • 1)先找到相同的子问题(函数头的设计)
  • 2)只关心某一个子问题是如何解决的(函数体的书写)
  • 3)递归出口

二、搜索vs深度优先遍历vs深度优先搜索vs宽度优先遍历vs宽度优先搜索vs暴搜

1、深度优先遍历vs深度优先搜索

其实,一句话就能概括下来:
遍历是形式,搜索是目的。

所以,我们平时说的深度优先遍历和深度优先搜索,其实他们俩是一样的。
都可以叫做dfs

2、宽度优先遍历vs宽度优先搜索

其实,一句话就能概括下来:
遍历是形式,搜索是目的。

所以,我们平时说的宽度优先遍历和宽度优先搜索,其实他们俩是一样的。
都可以叫做bfs

3、关系图

我们所说的搜索,其实就是暴搜。
在这里插入图片描述

4. 搜索问题的拓展

我们刚开始学习搜索时,总以为dfsbfs这两个搜索都只与二叉树有关。其实不然。
从下面的例题开始你会发现,很多东西都能使用dfs进行求解。

三、回溯与剪枝

这两个名词听起来貌似很高大上,其实用一个例子就能解释清楚了。

下面来看一个迷宫问题:

在这里插入图片描述

入口和出口如上:我们从入口出发,往右边走遇到墙壁之后,往下走。走到蓝色标记,也就是拐角点的地方后,这就是一个岔路口,此时我们有两种选择:

  • 1)往左边走
  • 2)往右边走

当我们选择往左边走时,如下图:
在这里插入图片描述
会遇到墙壁,此时我们就需要原路返回

这个从某一位置出发,一条道走到黑,再沿着原路返回的过程,就叫做回溯

回溯的这条路径,我们用绿色来标记。
在这里插入图片描述
此时又走到了蓝色拐点,在这个岔路口我们有三种选择:
1)往上走
2)往左走
3)往右走

可是,我们最初是从上面下来的,然后沿着左边走,走不通之后再返回来的。
所以,我们只有一个选择:往右走。

而这个判断的过程,也就是选择路径的过程,就叫做剪枝。

将往上走的路径剪掉,将往左走的路径剪掉,就是剪枝。

四、专题一

1. 汉诺塔问题

点我直达

在这里插入图片描述

算法分析

1.找到相同的子问题:


当n = 1时:

在这里插入图片描述

直接将盘子从A柱子挪到C柱子即可。


当n = 2 时

在这里插入图片描述
分为三步走:

1)我们需要将盘子a上面的盘子借助C柱子移动到B柱子。
在这里插入图片描述

2)将a盘子移动到C柱子上
在这里插入图片描述
3)将B柱子上的所有盘子借助A盘子移动到C柱子上。

在这里插入图片描述


当n = 3 时
在这里插入图片描述

与第二步相同:
分为三步走
1)将a盘子上面的所有盘子借助C柱子移动到B柱子上。
在这里插入图片描述

2)将a盘子移动到C柱子上。
在这里插入图片描述

3)将B柱子上面的所有盘子借助A柱子移动到C柱子上。

在这里插入图片描述

2.只关心某一个子问题如何解决。

所以我们会发现,当n >= 2时,都会执行相同的子问题的操作。操作如下:

  • 1)将a盘子上面的所有盘子通过C柱子挪到B柱子上。
  • 2)将a盘子挪到C盘子上。
  • 3)将B柱子上面的所有盘子挪到C柱子上。

在这整个过程中,你要相信一件事情:
你交给dfs这个函数的任务是:

我要把所有盘子全部借助一个柱子挪到另一个柱子上。

并且要相信dfs这个函数一定能完成这个任务。

这就是宏观看待问题的思路。

3.递归出口
递归出口就是当n = 1时,你会发现跟当n = 其他数的操作步骤是不一样的。
当n = 1时,直接将a盘子移动到C柱子即可。

代码编写

class Solution {
public:
//1.重复的子问题(函数头)
//要将A柱子上面的所有盘子借助B柱子全部转移到C柱子上面

//2.只关心某一个子问题在做什么(函数体)

//3.递归出口

    void dfs(vector<int>& A, vector<int>& B, vector<int>& C,int n) 
    {
        if(n == 1)
        {
            C.push_back(A.back());
            A.pop_back();
            return;
        }

        dfs(A,C,B,n-1);
        C.push_back(A.back());
        A.pop_back();
        dfs(B,A,C,n-1);
    }
    void hanota(vector<int>& A, vector<int>& B, vector<int>& C) 
    {
        int n = A.size();
        dfs(A,B,C,n);
    }
};

总结

提示:这里对文章进行总结:

本文章详细讲解了递归,搜索与回溯算法的入门理解级操作,以及通过一道例题感受一下dfs这种算法的强大之处,关键在于dfs写起来特别简单。

学好dfs,是进入大厂的必备技能。


http://www.niftyadmin.cn/n/5010802.html

相关文章

Docker从认识到实践再到底层原理(四-2)|Docker镜像仓库实战案例

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…

解决vagrant安装的centos7,在window主机重装系统过后,再次用vagrant启动centos7却无法启动

场景&#xff1a; vagrant安装的centos7&#xff0c;在window主机重装系统过后&#xff0c;再次用vagrant启动centos7却无法启动 检查 VirtualBox 版本&#xff1a;确保你安装的 VirtualBox 版本与 Vagrant 兼容。如果你更新了 VirtualBox&#xff0c;可能需要同时更新 Vagran…

ERROR 之 SpringMVC开发注解版之版本问题

如果你也和我一样&#xff0c;完全是按照狂神老师的代码来敲的&#xff0c;不用注解版的情况下是不会出错的&#xff0c;但是一用注解版&#xff0c;就出现了404&#xff0c;500的类型的错误。那我真诚的建议你换个jdk版本,再来试试。我试了3遍&#xff0c;事实证明用jdk1.8&am…

2023年及以后语言、视觉和生成模型的发展和展望

一、简述 在过去的十年里,研究人员都在追求类似的愿景——帮助人们更好地了解周围的世界,并帮助人们更好地了解周围的世界。把事情做完。我们希望建造功能更强大的机器,与人们合作完成各种各样的任务。各种任务。复杂的信息搜寻任务。创造性任务,例如创作音乐、绘制新图片或…

LeetCode 刷题记录——从零开始记录自己一些不会的

1. 最多可以摧毁的敌人城堡数目 题意 思路 两层循环&#xff0c;太low了 用一个变量记录前一个位置 代码 class Solution { public:int captureForts(vector<int>& forts) {int ans 0, pre -1;for (int i 0; i < forts.size(); i) {if (forts[i] 1 || forts…

ERROR: your rosdep installation has not been initialized yet

这个错误表示你的 rosdep 还没有初始化。rosdep 是一个 ROS 中的系统依赖管理工具,用于安装和配置需要的系统依赖包。在使用 rosdep 之前,需要先通过 rosdep update 命令初始化它。这个命令会连接远程服务器来更新 rosdep 的数据源,以获取所有支持的 ROS 版本和平台的依赖信息。…

linux信息查询

文章目录 信息查询查看cpu、内存、端口、进程查看网络 信息查询 查看系统负载:top&#xff0c; uptime&#xff0c;w&#xff0c;vmstat linux服务器性能负载查看&#xff08;cpu、内存、磁盘、网络&#xff09;&#xff1a;iostat&#xff0c;iotop,vmstat,free,sar 查看登陆…

Go基础11-理解Go语言的包导入

Go语言是使用包&#xff08;package&#xff09;作为基本单元来组织源码的&#xff0c;可以说一个Go程序就是由一些包链接在一起构建而成的。虽然与Java、Python等语言相比这算不上什么创新&#xff0c;但与祖辈C语言的头文件包含机制相比则是“先进”了许多。 编译速度快是这种…