递归--汉诺塔

递归

刚开始学习C语言的时候,感觉有着很多的难点,自学起来真的挺困难的,不过递归无疑是给我印象最为深刻的一个了,递归,简单了来说就是函数本身自己调用自己,当初看着这个定义的时候我觉得没啥难的,当真正了解到它的时候我才知道自己当初多么的天真.

递归的经典——汉诺塔问题

有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆: 每次只能移动一个圆盘; 大盘不能叠在小盘上面。 提示:可将圆盘临时置于B杆,也可将从A杆移出的圆盘重新移回A杆,但都必须遵循上述两条规则。

问:如何移?最少要移动多少次?

汉诺塔问题应该是每个刚开始接触递归的人都看过的一个问题,读者可以先想象一下不用递归的思想你会如何做出这道题,我目前依旧绕不清楚,如果不用递归,这道题对我来说就是千古难题,下面是用递归实现的代码,

#include<iostream>
using namespace std;
void p(char A, char B, char C, int n)
{
	if (n == 1)  cout << A << "->" << C << endl;
	else {
		p(A, C, B, n - 1);
		cout << A << "->" << C << endl;
		p(B, A, C, n - 1);
	}
}
int main()
{
	int n;
	cin >> n;
	char A = 'a', B = 'b', C = 'c';
	p(A, B, C, n);
	return 0;
}

看到这个代码量,自我感觉就是不会太难,如果读者有思考过上面的问题,再看这个代码,会觉得有点不可思议,怎么这么少,虽然这个代码量很少,但理解起来却十分困难,这个就是一个递归的基本问题,但你明白之后,你就会觉得这道题不是那么的困难.

首先 要了解并使用递归,就不要尝试去理解它,你越是理解它你就会越糊涂。
其次 不要想着去了解它的每一步,不要想着去一步一步的推导它的过程

以上两句话就是要明白一个道理:不要把自己放进递归函数中,放进一些简单的递归如阶乘,可能你还能理解,但是换个求全排列这样复杂点的递归函数你可能就懵逼了。你要站在一个上帝视角去看待问题,就像走迷宫,你不知道正确的路线就往里面走,那么你一定会迷路的,但是如果我站在高处的地方把正确的路线找到了,那我走迷宫的时候就不会迷路了。所以不要主观的看待,客观的把它当做一个人,你交给他的任务他一定能够完成的,你也千万别替他安排任务该如何去完成。https://blog.csdn.net/qq_36666756/article/details/82951772 讲的很好,可以去看一下

递归的过程可以这么分解-----递—>问题边界—>归
递: 也就是将问题传递下去下去
问题边界: 就是问题有答案的时候
归: 就是将答案传递回去

举个例子
假如你在电影院看电影,看到一半你忘记了自己坐在第几排,票也没在了,你又很想知道自己在哪一排,所以你就问了你前面的人,这时候你就将问题传给了他,但他也和你是一个情况,他又问了他前面的人,这样往复一直传递下去,##这个过程就是递的过程##假设这个问题一直传递到第一排,第一排的前面没有人,所以问题在这里得到答案,那么这里也就是问题边界,第一排的人把答案说给他后面的人,后面的人又说给他后面的人,知道答案传递回到你那,你获得了答案,你前面的人也获得了答案,这个过程也就是归的过程

上面的例子里,中间传递了多少人,你并不知道,但是,所以不要想着去了解它的每一步,不要想着去一步一步的推导它的过程,值得注意的一点是,上面的例子里,你所传递的问题和你前面的人传递的问题是一样的,这个就是使用递归的一个前提,能够将一个复杂的问题分解为多个相似且简单的问题

汉诺塔问题讲解

联系上面,汉诺塔问题用递归来解决的第一步就应该是将汉诺塔问题分解成多个相似的小问题。第二步,就应该找到汉诺塔问题的边界,这俩步完成,难么汉诺塔问题也就基本解决了

第一步——问题分解

首先,要将n个盘子借助b从a移动到c,那第一个移动到c上的盘子就应该是第n个盘子,那就又出现了一个问题,因为第n个盘子上面有n-1个盘子,要将第n个盘子移动到c上,就需要将n-1个盘子借助c从a移动到b,~~~~~~~~
这个问题就分解了,这个过程可能有点难理解,文笔不是太好,没有看懂欢迎私信我

//实现问题的传递
//定义函数   hanoi(char a,char b,char c,int n);
//a,b,c 表示柱子,n表示有n个盘子
//a,b,c,n  表示n个盘子从a借助b移动到c
//现在有n个盘子,调用函数的表示也就是
hanoi(a,b,c,n);   //n个盘子从a移动到c  那么传递给下一个的问题就应该是
hanoi(a,c,b,n-1);  //将n-1个盘子从a,借助c,移动到b
cout<<a<<"->"<<c<<endl;    //n-1个盘子移动到b后就可以将第n个盘子从a移动到c
hanoi(b,a,c,n-1);   //此时,n-1个盘子在b上,那也就是要将n-1个盘借助a从b上移动到c
//问题的传递解决 
寻找问题边界——递归的出口(归点)

将问题一直传递下去,到什么时候得到答案?
这个问题很好解决,也就是当n=1时,当n等于一时,所需要解决的问题就是将一个盘子从a移动到c,为什么能够确定就是从a移动到c呢,这个问题就需要回到函数本身,hanoi(char a,char b,char c,int n) 此时的a,b,c都是形参,也就是说它们的值是在变化的,而在问题传递的时候,hanoi(char a[1],char a[2],char a[3],int n) 都是将n个盘从a[1] 借助a[2] 移动到 a[3] ,也就是说,不管问题传递多少次,在每一次的问题中都是将n个盘从a借助b移动到c。那么出口就解决了,

if(n==1)   cout<<a<<"->"<<c<<endl;

到此,汉诺塔问题也就解决了。
***如果你相信你正在写的递归函数是正确的,并调用它,然后在此基础上写完这个递归函数,那么它就会是正确的,从而值得你相信它正确。***

斐波那契数列

尝试运用递归思想来解决斐波那契数列
菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和。
给出一个正整数k,要求菲波那契数列中第k个数是多少。

多加练习,学程序没有捷径,加油!
《下期讲一下 递归——全排列问题》 欢迎私信给介意

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值