在解决问题时如果我们需要多次重复的计算同一个问题,我们可以选择递归或者循环,递归是函数内部调用函数自身,直到一个终止条件,而循环是通过设置初始条件和终止条件,在一个范围内重复运算。比如求1+2+3+....+n;
递归算法如下:
int add_1toN_recursive(int n)
{
return n <= 1 ? 1 : n + add_1toN_recursive(n-1);
}
循环算法如下;
int add_1toN_iterative(int n)
{
int value = 0;
int i = 0;
for (i = 1; i <= n; ++i)
value+=i;
return value;
}
我们可以看出,递归代码很简洁明了,但是递归也有自身的致命缺点,递归由于是调用函数自身,需要进行压栈,保存参数、返回地址和临时变量,而且很多计算都是重复运算的,给性能带来很大的负面影响,如果调用层级太多,会导致栈溢出,出现segfault。
对于经典的斐波拉切数列,我们从数据结构书上可以很快写出:
int fibonacci(int n)
{
if (n <= 0)
return 0;
if (n == 1)
return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
但是如果n数值很大的话,速度慢是一回事,有时候会导致栈溢出,segfault出现,但是在树的前序、中序和后续遍历中,递归的实现却是比循环简洁好看多。
循环算法如下:
int fibonacci1(int n)
{
if (n <= 0)
return 0;
int f0 = 0;
int f1 = 1;
int fn = 0;
int i;
for (i = 2; i <= n; ++i)
{
fn = f0+f1;
f0 = f1;
f1 = fn;
}
return fn;
}
面试官又可能衍生出另外一种问法:一只青蛙跳台阶,一次可以跳1级,一次也可以跳2级,请问青蛙跳N级台阶有多少种跳法?
很简单,我们考虑简单的情况,假如只有1级台阶,青蛙显然只有一种跳法,如果有2级台阶,那就有两种跳法,一种是一次跳1级,跳两次;另一种是一次直接跳2级,那我们返回到一般情况讨论,把n级台阶当成n的函数,记为f(n),当n>2时,第一次跳就有两种不同选择,一是第一次只跳1级,此时跳法数目等于后面剩下的n-1级台阶的跳法数目,记为f(n-1);另外一种是第一次跳2级,此时跳法数目等于后面剩下的n-2级台阶的跳法数目,记为f(n-2),由此我们推算出n级台阶的不同跳法总数是f(n)=f(n-1)+f(n-2),可以看出这个就是斐波拉切数列。