c语言回顾-函数递归(上):https://developer.aliyun.com/article/1624395
3.递归与迭代
递归是一种很好的编程技巧,但是很多技巧一样,也是可能被误用的,就像举例1一样,看到推导的公 式,很容易就被写成递归的形式:
int Fact(int n) { if(n==0) return 1; else return n*Fact(n-1); }
Fact函数是可以产生正确的结果,但是在递归函数调用的过程中涉及一些运行时的开销。
在C语言中每一次函数调用,都要需要为本次函数调用在栈区申请一块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧。
函数不返回,函数对应的栈帧空间就一直占用,所以如果函数调用中存在递归调用的话,每一次递归函数调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。 所以如果采用函数递归的方式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢 出(stack overflow)的问题。
所以如果不想使用递归就得想其他的办法,通常就是迭代的方式(通常就是循环的方式)。
像上面求n的阶乘也可以用迭代的方法
int Fact(int n) { int i = 0; int ret = 1; for(i=1; i<=n; i++) { ret *= i; } return ret; }
事实上,我们看到的许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更加清晰, 但是这些问题的迭代实现往往比递归实现效率更高。
当一个问题非常复杂,难以使用迭代的方式实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。
典型的例子就是求斐波拉契数列
举例3:求第n个斐波那契数
我们也能举出更加极端的例子,就像计算第n个斐波那契数,是不适合使用递归求解的,但是斐波那契数的问题通过是使用递归的形式描述的,如下:
Fib(n)= 1(n<=2)
=Fib(n-1)+Fib(n-2) (n>2)
int Fib(int n) { if(n<=2) return 1; else return Fib(n-1)+Fib(n-2); }
但是当我们n输入为50的时候,需要很长时间才能算出结果,这也说明递归的写法是非常低效的,那是为什么呢?
其实递归程序会不断的展开,在展开的过程中,我们很容易就能发现,在递归的过程中会有重复计算,而且递归层次越深,冗余计算就会越多。可以通过代码测试:
#include <stdio.h> int count = 0; int Fib(int n) { if(n == 3) count++;//统计第3个斐波那契数被计算的次数 if(n<=2) return 1; else return Fib(n-1)+Fib(n-2); } int main() { int n = 0; scanf("%d", &n); int ret = Fib(n); printf("%d\n", ret); printf("\ncount = %d\n", count); return 0; }
这里我们看到了,在计算第40个斐波那契数的时候,使用递归方式,第3个斐波那契数就被重复计算了39088169次,这些计算是⾮常冗余的。所以斐波那契数的计算,使用递归是非常不明智的,我们就得 想迭代的方式解决。
我们知道斐波那契数的前2个数都1,然后前2个数相加就是第3个数,那么我们从前往后,从小到大计算就行了。
int Fib(int n) { int a = 1, b = 1, c =0; while (n > 2) { c = a + b; a = b; b = c; n--; } return c; }
通过迭代方法计算,效率会高很多!!!
OK,本节内容到此结束,递归的理解重点是要画图。
支持小编的友友留下三连和评论吧!!!