【递归】1.汉诺塔问题

cpp 复制代码
面试题 08.06. 汉诺塔问题
leetcode链接:https://leetcode.cn/problems/hanota-lcci/description/

在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。
一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。
移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。

请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。

你需要原地修改栈。

示例1:

 输入:A = [2, 1, 0], B = [], C = []
 输出:C = [2, 1, 0]
示例2:

 输入:A = [1, 0], B = [], C = []
 输出:C = [1, 0]
提示:

A中盘子的数目不大于14个。

本文章主要围绕两个问题来展开:
1.为什么汉诺塔问题能使用递归?
2.如何解决汉诺塔问题?

++为什么汉诺塔问题能使用递归?++

可以使用递归的题目,可以总结为一句话:
解决大问题,可以使用相同类型的子问题

解决子问题 ,可以使用相同类型的子问题

我们来对汉诺塔问题进行分析:
当N = 1时,移动的策略:

直接将A中的盘子转移到C中

当N = 2时,移动的策略:

1.将A上面的第一个盘子移动到B上,

2.将A上面的第二个盘子移动到C上

3.将B上面的盘子移动到C上

当N = 3时,移动的策略:
1.将A上面的第一个盘子移动到C上
2.将A上面的第二个盘子移动到B上
3.将C上面的第一个盘子移动到B上

4.将A上面的第一个盘子移动到C上

5.将B上面的第一个盘子移动到A上
6.将B上面的第二个盘子移动到C上
7.将A上面的第一个盘子移动到C上

通过上面的演示,可以得到什么?

下面的三步
1.将A上面的第一个盘子移动到C上
2.将A上面的第二个盘子移动到B上
3.将C上面的第一个盘子移动到B上

N = 3时,前面的三步就是将A中的N-1个盘子借助C移动到B上,而这个操作,就是和N = 2时一模一样的。

而下面的这三步,
5.将B上面的第一个盘子移动到A上
6.将B上面的第二个盘子移动到C上
7.将A上面的第一个盘子移动到C上

N = 3时,这三步就是将B中的N-1个盘子借助A移动到C上,而这个操作,就是和N = 2时一模一样的。

++因此,本问题的流程就抽象成什么了?++

当解决N = 3时,可以用N = 2的策略 --> 当解决N个盘子的问题时,可以使用N - 1个盘子的策略。

++而我前面提到的递归的本质:++
解决大问题,可以使用相同类型的子问题

解决子问题 ,可以使用相同类型的子问题

++如何写递归的代码??++

写递归的代码,需要三个部分:

cpp 复制代码
1.挖掘出重复的子问题  --> 函数头

2.只关心某一个子问题在做什么  --> 函数体

3.递归的出口 --> 防止死递归

1.挖掘出重复的子问题 --> 函数头

根据前面提到的n = 3时如何转化子问题的内容,将流程抽象出来,就变成了:

N = 3时,

cpp 复制代码
第一步:将A中的N - 1个盘子借助C转移到B上 (重复的子问题)
第二步:将A中的最后一个盘子转移到C上
第三步: 将B中的N - 1个盘子借助A转移到C上 (重复的子问题)

我们根据重复的子问题设计出函数头。

从上面可以发现,有4个参数,分别是A,B,C和盘子的数量N

因此,函数头设计为:

2.只关心某一个子问题在做什么 --> 函数体

N = 3时,

cpp 复制代码
第一步:将A中的N - 1个盘子借助C转移到B上 (重复的子问题)
第二步:将A中的最后一个盘子转移到C上
第三步: 将B中的N - 1个盘子借助A转移到C上 (重复的子问题)

我们要从宏观去思考,不要思考dfs如何实现这个任务,把自己当成领导,相信dfs一定能完成你布置的任务。

cpp 复制代码
给dfs布置要给任务,相信dfs一定能完成任务 。

3.递归的出口 --> 防止死递归

n = 1时,不需要借助其他盘子,直接将A上的盘子转移到C上

代码:

cpp 复制代码
class Solution {
public:
    void hanota(vector<int>& a, vector<int>& b, vector<int>& c) {
        dfs(a, b, c, a.size());
    }

    void dfs(vector<int>& a, vector<int>& b, vector<int>& c, int n)
    {
        if (n == 1)
        {
            //直接将a中的盘子转移到c上
            c.push_back(a.back());
            a.pop_back();
            return;
        }

        //当有n个盘子的时候
        dfs(a, c, b, n - 1);   //将a中的n-1个盘子,借助c,转移到b上

        //将a中的最后一个盘子转移到c上
        c.push_back(a.back());
        a.pop_back();

        //将b中的n-1个盘子,借助a,转移到c上
        dfs(b, a, c, n - 1);
    }
};

总结

++1.什么问题能使用递归?++
解决大问题,可以使用相同类型的子问题

解决子问题 ,可以使用相同类型的子问题
++2.如果要写递归,需要哪几步?++

cpp 复制代码
第一步:挖掘出相同的子问题  (关系到具体函数头的设计
第二步:只关心具体子问题做了什么  (关系到具体函数体怎么写,是一个宏观的过程)
第三步:找到递归的出口,防止死递归  (关系到如何跳出递归)
相关推荐
此生只爱蛋13 分钟前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
何曾参静谧33 分钟前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++
咕咕吖44 分钟前
对称二叉树(力扣101)
算法·leetcode·职场和发展
九圣残炎1 小时前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode
lulu_gh_yu1 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
ULTRA??2 小时前
C加加中的结构化绑定(解包,折叠展开)
开发语言·c++
凌云行者3 小时前
OpenGL入门005——使用Shader类管理着色器
c++·cmake·opengl
凌云行者3 小时前
OpenGL入门006——着色器在纹理混合中的应用
c++·cmake·opengl
~yY…s<#>3 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode
可均可可4 小时前
C++之OpenCV入门到提高004:Mat 对象的使用
c++·opencv·mat·imread·imwrite