递归算法
递归看这篇就够了
引子
我怀着无比激动的心情写下这篇文章,因为我它介绍是一种无比优雅的问题解决方案——递归
相信很多同学在学习递归的时候都会有这样一个疑惑,这个东西究竟是如何自己调用自己的,大佬写几行代码就实现了复杂的功能。今天我们就来好好讨论这个优雅的递归。
一、定义
在数学与计算机科学中,递归(Recursion)
是指在函数的定义中使用函数自身的方法。实际上,递归,顾名思义,其包含了两个意思:递 和 归,这正是递归思想的精华所在。
用人话来说就是函数调用自己。
二、递归的三大要素
- 明确这个函数作用 先不管代码,我们首先要明白的是我们要这个函数干什么。
例如我定义一个函数
void f(int i){
//计算阶乘
}
这个函数就是计算n的阶乘。好啦,现在我们已经明白了这个函数的作用,我们来看看第二要素吧。
- 找到终止条件 我们需要给函数一个终止条件,总不能让函数一直在调用自己,在函数‘递’的这个过程结束后,拿着结果进行‘归’的过程。
//我们假设i=1
void f(int i){
if(i==1){
return 1;
}
}
由上面的栗子我们可以直接看出当i=1时候,函数的返回值相信大家非常清楚。那我们现在来看看第三要素吧,
-
提取重复的逻辑 不断的缩小范围,我们可以使用一些变量或操作使原函数结果不变。
好啦,现在重要到第三步了,当然这一步也是有一丢丢难的,找不清等价关系式没关系,因为你不是天才,你需要多接触几道题,我在后面也会增加10道递归算法题的。话不多说我们看代码。
void f(int i){
if(i==1) //递归结束的条件
return 1;
//函数的等价操作
return f(i-1)*i;
}
f(i-1)就是函数的等价式,我们通过不断缩小i的值,这样范围就变小了。为了不改变原函数的结果,我们还需要进行*i。
三、递归执行
看到这里,我相信大家对于递归的定义和递归的要素都已经很清楚,那么,我们接下来我们就来谈谈递归的执行吧。
递归程序的运行过程可以分为两个阶段
- 递推阶段 递归每一次都是基于上一次进行下一次的执行。
- 回溯阶段 当遇到终止条件,则从最后往回一级一级的把值返回来。
我们还是以求i的阶乘为例子吧。
void func(int i){
if(i==1) //递归结束条件
return 1;
return f(i-1)*i; //关系等价式
}
通过画图我们可以清楚的看到递归的运行过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7JRynTKq-1607070567913)(https://imgkr2.cn-bj.ufileos.com/892f8e2f-bec1-4501-95ad-1e7ab2d9cbcc.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=waZiwh%252FiYuCsKWrIzPJKX4xNO8c%253D&Expires=1607147707)]
通过看图我们不难发现func()函数一直在调用自己,当i=1时,递推结束,函数开始一层一层回溯。所以在使用递归来解决问题时候我们一定要注意递归结束的条件,如果没有结束条件的话,无限递归会造成的堆栈溢出的。
四、经典递归问题实战
(1)斐波拉契数列
斐波纳契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、……
这里我们使用两种方法来解决这个问题,通过这两种方式,我想大家能够感受到递归的妙处。
非递归实现方法
//非递归实现
int func(int n){
if(n==1||n==2){
return 1;
}
int result = -1;
int first = 1;
int second = 1;
for (int i = 3; i <= n; i++) { // 循环
result = first + second;
first = second;
second = result;
}
return result;
}
递归实现
int func(int n){
if(n==1||n==2){ //递归出口
return 1;
}
return func(n-1)+func(n-2);
}
我们不难发现使用使用递归的代码比非递归的代码要简洁的多,同时也能看出递归的代码我们通过循环也能完成。现在引出了我们的下一个问题。
递归和循环我们应该怎么选择?
在我看来,递归和循环解决问题都是一样的,递归能使解决方案更加清晰,但是递归在性能上没有优势。实际上,在开发中,有些情况使用循环比递归显得更好。如果使用循环,程序的性能将会更好;但是使用递归,程序将会更加容易理解。如何选择看什么对你更加重要。
好啦!今天的文章就到这里啦,这篇文章上个礼拜就开始写了,奈何最近考试太多了。好在,复习了一个礼拜,感觉自己答的还是可以的。看来考前突击很重要的(敲重点)。今天难得有空,终于把文章写完了。如有错误欢迎大家指出。如果本篇文章对你有收获,那么就点个再看再走吧。
作者 :Arleny
CSDN : Keiy_
点个关注在走吧,我们下期再见。