0
点赞
收藏
分享

微信扫一扫

递归思想如何理解?

江南北 2022-04-26 阅读 123

相信很多初学的伙伴对递归是又爱又恨,递归能很轻松的解决一些复杂问题,但是理解起来太过抽象,对新手小白很不友好,今天这篇博客就让我来为大家分享一下我学习递归的心得和在学习过程中的一些误区,希望这篇博客能够帮到你;

             因为自己淋过雨,所以也想为别人撑伞!

要理解递归首先要明白什么是递归?

递归,在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。

这只是网上随便都能搜到的官方解释,对很多伙伴帮助并不大,该懵逼还是懵逼,那我们不妨从俗套的现实问题中理解递归微妙的过程:

   你想将心仪已久的女神拿下,女神很喜欢编程,这个时候你想将自己的女神追到手要会编程吧(有共同话题才能有机会嘛)但是会编程必须得学数据结构和算法,学数据结构和算法又必须得会一门语言,学习语言要了解它基本的语法,在语法之下我们应该去好好了解计算机的底层操作原理,一但掌握底层操作原理后我们就可以往回一步一步的去实现我们的目标,最后成为编程大牛拿下女神!

当然还有一种情况,女神可能对你并不会设置跳出条件,就算你是编程大牛了也不会做你女朋友,有些兄弟可能在这个过程中不断的寻找让她回心转意的条件,这样注定没有结果的死循环就是死递归;

让我们回到计算机中,正确的递归一定有两个条件,有这两个条件不一定对,但没有这两个条件一定是错误的递归:

1.递归一定有一个跳出的条件,当达到这个条件的时候将不会递下去了,而是归来;

2.每次递归一定是逐渐接近这个跳出条件的;

我们来看编程中递归的例子:

输入一个整数n如何打印它的每一位数字?

如何用递归实现?

经典汉诺塔递归问题:

汉诺塔是一个非常具有代表性的递归问题,递归在这里体现的非常精妙,看完后你一定会对递归有一个更深层次的理解

问有三个柱子x y z,x上有n个由小到大的盘子,一次只能移动一个盘子,并且每次移动盘子小的必须在大盘子上面,怎么才能将x柱子上的所有盘子移动到z柱子上?

没玩过汉诺塔游戏的可以进链接试玩一下:汉诺塔,汉诺塔游戏在线玩_7k7k益智游戏_7k7k小游戏

多玩几遍我们发现它是有规律,这个规律我们抽象的分为以下三个步骤:

1.首先要将n-1个盘子借助z柱子将他们放到y柱子上;

 2.此时x柱子上面只剩下了第n个盘子,将第n个盘子直接移动到z柱子上面;

3.当最大的盘子移动到z柱子上了我们就不要管它了,现在我们重新复盘我们的问题,是不是在原问题上变成了如何将y柱子上的n-1个盘子经由x柱子移动到z柱子上面,和原问题一模一样,只是盘子个数削减了还有柱子参数不一样而已

 汉诺塔的思想就是这样三步抽象的过程,那我们用代码来实现它:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void move(char pa1, char pa2)
{
	printf("%c -> %c \n",pa1,pa2);
}
void Hanoi(int n, char x, char y, char z)
{
	if (n == 1)//当盘子为一个直接从x移动到z
	{
		move(x, z);//写一个打印函数
	}
	else
	{
		Hanoi(n - 1, x, z, y);//将n-1个盘子从x经由z移动到y
		move(x, z);// 将x上的最后一个盘子直接移动到z
		Hanoi(n - 1, y, x, z);//将n-1个盘子从y经由x移动到z
	}
}
int main()
{

	char x = 'x';
	char y = 'y';
	char z = 'z';
	int n = 0;
	scanf("%d", &n);
	Hanoi(n, x, y, z);
	return 0;
}

核心代码就是这一小段代码:

        Hanoi(n - 1, x, z, y);       ----->           //将n-1个盘子从x经由z移动到y
        move(x, z);                      ----->           // 将x上的最后一个盘子直接移动到z
        Hanoi(n - 1, y, x, z);         ----->          //将n-1个盘子从y经由x移动到z

这三个代码就代表汉诺塔的三个抽象步骤,其实我当时到这里还是非常的懵逼,它怎么就会知道盘子该如何如何移动,就凭这几段代码,太不可思议了!                                                                  下面我就给大家分享一个知乎上叫Fireman A用户的评论

我看完后受到不少帮助,确实我们的思维有时候很执拗,非要跟踪全局,越是这样递归越是能把你绕晕,相反按照这个老哥的思路揭下递归的面具,面具之下也是一张普通的脸,当然递归的熟练不是几篇博客能够解决的,知识的熟练没有捷径可走,大家下来一定还是要多看多练。

接下来我们来看一个递归和非递归解决第n个斐波那契数列的问题:

每个斐波那契数列都是前两个斐波那契数之和,1   1   2    3    5     8     13     21     34 ...........        

int Fibonacci(int n)  //非递归
{
	if (n < 3) //当n小于3时,前两个数列一定是1
	{
		return 1;
	}
	int tmp1 = 1;//定义第一个数列
	int tmp2 = 1;//定义第二个数列
	int snm = 0; //定义一个数将第一.二个数列相加
	while (n > 2)
	{
		snm = tmp1 + tmp2;
		tmp1 = tmp2;
		tmp2 = snm;
		n--;
	}
	return snm;
}

int Fibonacci(n) //递归
{
	if (n < 3)
	{
		return 1;
	}
	return Fibonacci(n - 1) + Fibonacci(n - 2);
}

看上去递归的代码好像很简短,其实在递归内它进行了大量的重复计算,效率远远不及非递归的写法,递归将一个主问题不断的分成两个递归子问题,子问题又不断的将分成两个更小的问题,这样反复下去,问题就变复杂很多了

有没有发现,图片中有很多重复计算的过程,如果我是求第50个斐波那契数列呢?重复的过程可以超过千万,就算是计算机也要不少时间才能计算完,这样的效率我们是不能认可的,所以并不是所有的问题都要去用递归解决,要切合实际开发,灵活的运用递归;

另外肌肉是锻炼出来的,运动指南永远只是辅助而已,你不行动起来肌肉如何凭空长出来!

举报

相关推荐

0 条评论