【递归】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 复制代码
第一步:挖掘出相同的子问题  (关系到具体函数头的设计
第二步:只关心具体子问题做了什么  (关系到具体函数体怎么写,是一个宏观的过程)
第三步:找到递归的出口,防止死递归  (关系到如何跳出递归)
相关推荐
Milo_K1 小时前
今日 leetCode 15.三数之和
算法·leetcode
Darling_001 小时前
LeetCode_sql_day28(1767.寻找没有被执行的任务对)
sql·算法·leetcode
AlexMercer10121 小时前
【C++】二、数据类型 (同C)
c语言·开发语言·数据结构·c++·笔记·算法
蓑 羽1 小时前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
源代码:趴菜1 小时前
LeetCode63:不同路径II
算法·leetcode·职场和发展
小灰灰爱代码2 小时前
C++——求3个数中最大的数(分别考虑整数、双精度数、长整数的情况),用函数模板来实现。
开发语言·c++·算法
南加第一划水2 小时前
Leetcode 每日一题:Evaluate Division
算法·leetcode·职场和发展
chnyi6_ya2 小时前
一些写leetcode的笔记
笔记·leetcode·c#
zero_one_Machel2 小时前
leetcode73矩阵置零
算法·leetcode·矩阵
BeyondESH3 小时前
Linux线程同步—竞态条件和互斥锁(C语言)
linux·服务器·c++