汉诺塔问题(递归超详细)C++,leetcode


前言

在本文章中,我们将要详细介绍一下汉诺塔问题,本题目我们采用递归的方式解决相关的内容

递归介绍

递归:函数自己调用自己

本质:主问题可以划分为相同的子问题。

我们之后看待递归要进行宏观看待

🌟 不要在意递归的细节展开图
🌟 把递归的函数当成一个黑盒
🌟 相信这个黑盒一定能完成这个任务

如何写好一个递归

🌟 先找到相同子问题---->函数头的设计
🌟 只关心某个子问题是如何解决的----->函数体
🌟 注意递归函数的出口

BFS(宽搜)
DFS(深搜)

一、题目分析

在这里插入图片描述
题目要求详解:
  🌟 有三个柱子假设名称分别为a,b,c,其中a柱子上有若干个盘子。
  🌟这些盘子按照从底往上大小依次减小的次序摆放。
  🌟不论盘子在哪个柱子上都必须要求下面盘子的大小都大于上面盘子的大小
  🌟将a柱子上的若干盘子通过b柱子按照要求转移到柱子上

二、算法原理

☀️ ☀️ 学过的小伙伴都知道这道题目可以用递归解决,那我们为什么要用递归算法解决,为什么不用贪心,动态规划等算法呢??

1.为什么要用递归

首先简单看一下递归:函数自己调用自己,将主问题分解为相同子问题,子问题又可以调用相同的子问题。

我们要有规律的放挪动盘子,每一步的意义是什么,同时注意不要分解的太过于细致

把每一步分析一下
🔱🔱n==1,直接可以把a柱子上的盘子放到c柱子上,不用借助b柱子就可以实现
在这里插入图片描述

🔱🔱n==2,题目要求我们把a柱子上的2个盘子通过b柱子转移到c柱子上。

  💗💗首先我们要把a柱子上的盘子挪到c柱子上,先想办法把a柱子上最下面那个最大的盘子挪到c柱子上,剩余的那一个才可以挪过去。
  💗💗要挪动a柱子上最大的盘子,得先把a柱子上那个小盘子挪走,那这个怎末挪动呢??
  💗💗我们发现这一步和n== 1时的情景非常类似。按照n==1的步骤,把那个小盘子直接挪到b柱子上。
  💗💗这时我们就可以把a柱子上最大的那个盘子挪到c柱子上了。
  💗💗最后把那个小盘子挪动c柱子上就可以了
在这里插入图片描述

🔱🔱n==3,题目要求我们把a柱子上的3个盘子通过b柱子转移到c柱子上。

  💗💗首先我们要把a柱子上的盘子挪到c柱子上,先想办法把a柱子上最下面那个最大的盘子挪到c柱子上,剩余的那俩个才可以挪过去。
  💗💗要挪动a柱子上最大的盘子,得先把a柱子上那两个盘子挪走,那这个怎末挪动呢??
  💗💗我们发现这一步和n== 2时的情景非常类似。按照n==2的步骤,把那俩个小盘子通过c柱子挪动到b柱子上(不能挪动到c柱子上,我们需要用这个柱子放最大盘子)。
  💗💗这时我们就可以把a柱子上最大的那个盘子挪到c柱子上了。
  💗💗最后把那个俩盘子通过a柱子挪动c柱子上就可以了
在这里插入图片描述

🔱🔱n==N,题目要求我们把a柱子上的N个盘子通过b柱子转移到c柱子上。

  💗💗首先我们要把a柱子上的盘子挪到c柱子上,先想办法把a柱子上最下面那个最大的盘子挪到c柱子上,剩余的那N-1个才可以挪过去。
  💗💗要挪动a柱子上最大的盘子,得先把a柱子上那N-1个盘子挪走,那这个怎末挪动呢??
  💗💗我们发现这一步和n== N-1时的情景非常类似。按照n==N-1的步骤,把那N-1个小盘子通过c柱子挪动到b柱子上(不能挪动到c柱子上,我们需要用这个柱子放最大盘子)。
  💗💗这时我们就可以把a柱子上最大的那个盘子挪到c柱子上了。
  💗💗最后把那N-1盘子通过a柱子挪动c柱子上就可以了

通过这里我们发现,他们采用的相同的方式实现,这与递归的定义是一致的。

递归是一种在函数定义中使用函数自身的方法。在递归过程中,函数将问题分解为更小的子问题,并通过调用自身来解决这些子问题。递归通常用于解决可以被分解为相同类型的子问题的问题,直到达到基本情况为止。

2.如何编写代码

💫 💫.重复子问题----->函数头

将a柱子上的N个盘子借助b柱子转移c柱子上(知道递归需要哪些参数)
dfs(a,b,c,n);

💫 💫.只关心某个问题----->函数体

把递归函数当成一个黑盒,相信他一定能完成任务

1.先把a柱子上除去最后一个盘子通过c柱子挪到b柱子上–>dfs(a,c,b,n-1);
2.把a柱子剩余的挪动到c柱子上------> c.push_back(a.back()); a.pop_back();
3.把b柱子上的盘子通过a柱子转移到c柱子上–》dfs(b,a,c,n-1);

💫 💫.递归出口---》到哪不可再分

我们发现仅仅剩下一个盘子就不可再分,那麽也就是n==1就是递归出口,把a柱子盘子直接放在c柱子上

n==1–>c.push_back(a.back()); a.pop_back();

三、代码实现

class Solution {
public:
    void hanota(vector<int>& a, vector<int>& b, vector<int>& c) 
    {
        dfs(a,a.size(),b,c);
    }
    void dfs(vector<int>& a, int n,vector<int>& b, vector<int>& c)
    {
        if(n==1)
        {
            c.push_back(a.back());
            a.pop_back();
            return;
        }
        dfs(a,n-1,c,b);
        c.push_back(a.back());
        a.pop_back();

        dfs(b,n-1,a,c);

    }
};

四、对比递归与循环

我们可以发现递归和循环都是解决重复子问题的。

二者是可以进行相互替换的,那什么时候用循环比较合适,神魔时候用递归更好呢??

递归其实就是深度优先遍历
如果由多个分支的情况下,我们采用递归是更好的,如果想用循环实现,我们需要借助栈来完成。
如果只有单分支,循环是更加方便的。
中序遍历只存在于二叉树中。

总结

以上就是我们对Leetcode中汉诺塔问题详细介绍,希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lim 鹏哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值