汉诺塔问题(递归算法思想)

前言

  • 🛕“汉诺塔问题”是运用递归思想解决问题的经典例题,非常值得我们去深入地去理解递归的思想!
  • 递归的核心思想就是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解

汉诺塔问题的起源:

汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。不论白天黑夜,总有一个僧侣在移动这些圆盘,一次只移动一个,小的圆盘必须在大的上面。僧侣们预言,当所有的圆盘都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而庙宇和众生也都将同归于尽……

动态图演示:
在这里插入图片描述

递归思想

把一个大型复杂问题层层转化为一个与原问题相识,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是把大事化小的过程。递归中的递就是递推的意思,归就是回归的意思。

举例理解:求n的阶乘

把一个较大的问题,转换为一个与原问题相似,但规模较小的问题来求解的。
在这里插入图片描述
5!相比与4!,就是一个较大的问题,因此就可以把5!进一步转化成为一个它更小的问题。同理,n! = n*(n-1)!;(n-1)!又可以进一步拆分,直到n为1或者为0时,不再拆分。由此可见,递归的时间复杂度并不低,效率可能不是很高。

那我们就可以写出函数Fact求n的阶乘,假设Fact(n)就是求n的阶乘,那么Fact(n-1)就是求n-1的阶乘,函数如下:

int Fact(int n)
{
	if(n<=0)
		return 1;
	else
		return n*Fact(n-1);
}

汉诺塔问题分析:

左为A中间为B右为C
1、当柱子上只有一个圆盘时(即n=1),就非常简单,直接从左边(A)移动到右边(C)即可。
在这里插入图片描述

在这里插入图片描述

2、当柱子上有两个圆盘时(即n=2)

  • 第一步先把小圆盘从左(A)移动到中间(B)
  • 第二步再将大圆盘从左(A)移动到右边(C)
  • 第三步将在中间(B)的小圆盘移动到右(C)
    在这里插入图片描述
    在这里插入图片描述

3、当柱子上有三个圆盘时(即n=3),这个时候如果我们一个一个地去分析,情况就开始变得有点复杂了。

  • 第一步先把上面两个从左(A)先移动到中间(B)(它俩怎样移动先不考虑)
  • 第二步将底下最大的圆盘移动到右边(C
  • 第三步再将中间(B)的两个圆盘移动到右边(C
  • 其中第二步可以直接实现,而第一步和第三步还需继续递归
  • 第一步中在左(A)的两个圆盘通过右(C)移动到中间(B)
  • 第三步中在中间(B)的两个圆盘通过左(A)移动到右(C)
    在这里插入图片描述
    在这里插入图片描述

4、顺着这样的思路

  • 如果柱子上面有n个圆盘,依然可以先把上面的n-1个看作一个整体,通过右(C)移动到中间(B)
  • 把第n个圆盘(最底下的圆盘)移动到右边(C)
  • 然后中间(B)n-1个圆盘又可以把上面的n-2个圆盘继续用递归的思想继续拆分,直到圆盘全部移动到右边(C)

在这里插入图片描述

代码实现:

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>

void Hanoi(int n, char A, char B, char C)//将n个圆盘从左边(A)借助中间(B)全部移动到右边(C)
{
	if (n == 1)//当只有1个圆盘时
		printf("%c->%c\n", A, C);//直接将左边(A)的圆盘移动到右边(C)
	else//当n>=2时
	{
		Hanoi(n - 1, A, C, B);//将左边(A)上面的n-1个圆盘借助右边(C)移动到中间(B),
		                      //一直递推,直到左(A)上除了底下那个外,其它全部移动到中间(B)
		                      
		printf("%c->%c\n", A, C);//将左边(A)上剩下的1个圆盘移动到右边(C)
		
		Hanoi(n - 1, B, A, C);//将中间(B)上的n-1个圆盘借助左边(A)移动到右边(C),
		                      //一直递推,直到中间(B)上的圆盘全部的圆盘全部移动到右(C),结束!
	}
}

int main()
{
	int n = 0;
	scanf("%d", &n);//输入n个圆盘
	Hanoi(n, 'A', 'B', 'C');
	return 0;
}

总结:

1、第一步移动n-1小盘子从左(A)移动到中间(B)(又可以递归)出第一、二、三步,一直递推,直到左(A)上除了底下那个外,其它全部移动到中间(B)
2、第二步移动第n个小盘子从左(A)移动到右(C)(直接实现)
3、第三步移动n-1小盘子从中间(B)移动到右(C)(又可以递归)出第一、二、三步,一直递推,直到中间(B)上的圆盘全部的圆盘全部移动到右(C),结束!

  • 把n-1个圆盘看作为一个整体,再把n-2个圆盘看作一个整体,再把…,直到达到结束条件!
  • 递归就是要大化小,繁化简
  • 运用递归思想就是让一个函数在它的函数体内调用它自己。
  • 递归会使得函数反复调用自身,每次调用都会在函数栈帧上开辟一块空间,一直递推,直到遇到结束条件。
  • 递归有两个条件,递推关系和结束条件

以上就是我们在学习递归算法过程中必定要遇见的汉诺塔问题的解题思想,希望对大家有帮助,如果喜欢博主写的博客,希望动动你们的小手,帮博主点个赞,你们的支持是我最大的创作动力!最后还要提醒一下,递归虽好,但是也会引入一些问题,⼀定不要迷恋递归,适可而止就好。

  • 61
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 28
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小覃同學

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

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

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

打赏作者

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

抵扣说明:

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

余额充值