您的位置:首页 > Web前端

剑指offer-09:递归和循环,斐波那契数列

2017-08-08 11:51 387 查看
当需要重复的计算相同问题时,通常可用递归或者循环迭代两种办法。

如计算1+2+…+N问题。

// 递归实现
int add1toN_recursive(int n)
{
return n <= 0 ? 0 : (n + add1toN_recursive(n-1));
}


// 循环实现
int add1toN_Iterative(int n)
{
int result = 0;
for(int i = 0; i<n; ++i)
result += i;

return result;
}


两种都可以做。递归代码简洁,但开销大,并可能导致栈溢出。当N=10000时递归不能正常运行,但循环可以。另外递归把问题分解为子问题求解,子问题间可能有重叠部分,就会存在重复计算。

计算斐波那契数列的第n项。

f(n) =

0, n=0;

1, n = 1;

f(n-1)+f(n-2), n>1

根据其定义非常清晰的写出其递归代码:

long long Fibonacci(unsigned int n)
{
if(n <= 0)
return 0;
if(n == 1)
return 1;

return Fibonacci(n-1) + Fibonacci(n-2);
}


这说明要玩好递归,必须找到子问题,能像上面那样写出来,递归就没问题。

n试着输个10,结果是55。试着输个100,用秒的时间来等。效率太差!包含大量重复问题。算f(9)会算f(8)和f(7),算f(8)会算f(7)和f(6)。

递归看着很牛逼,但谁用谁知道。有没有实用的方法?当然,想想人是怎么计算递归的?不就是从头开始加到N吗?时间复杂度O(n)。

long long Fibonacci_ite(unsigned int n)
{
// 第1项和第2项,直接返回值
int result[2] = {0, 1};
if(n < 2)
return result
;

long long fib_1 = 0;
long long fib_2 = 1;
long long fib_n = 0;
// 从第3项开始迭代往后求
for(unsigned int i = 2; i <= n; ++i)
{
fib_n = fib_1 + fib_2;
// 更新本次值以备下次
fib_1 = fib_2;
fib_2 = fib_n;
}

return fib_n;
}


这当然不是最快的。并没有发挥数学的作用。数学的厉害之处在于从1加到N问题中像小高斯1分钟就算完的题目那样高效率。

同样的,斐波那契数列有数学上的归纳法计算公式:

[f(n)f(n−1)f(n−1)f(n−2)]=[1110]n−1

这说明只要计算出右边矩阵的n-1次方,则第一项为f(n)的值。如果简单从0开始循环乘方,时间复杂度仍为O(n)。

然而乘方可以用平方来计算。要算4次方,则只需要算出2次方,再平方即可。这样时间复杂度减至O(logn)。

但是隐含的时间常数巨大,代码复杂,实际中不太使用。仅做知识面展示。

实现链接:http://blog.csdn.net/dadoneo/article/details/6776272

斐波那契数列可以加上很多应用场景。

例1:一只青蛙一次可以跳上1级台阶,也可跳上2级台阶,求跳上n级台阶总共有多少种跳法?

答:

若只有1级,则1种;若有2级,则2种跳法。

把n级台阶的跳法看成n的函数f(n)。当n大于2时,因为第一次可2种跳法,则分两种情况。每种情况下的子问题分别是f(n−1)和f(n−2)。则n>2时:

f(n)=f(n−1)+f(n−2)

这就是个斐波那契数列。

若青蛙一次可以跳1级,也可2级,…,也可n级,多少种?

答:问题抽象为:

f(n)=f(n−1)+f(n−2)+...f(1)+f(0)f(1)=1,f(0)=1

f(0)=1f(1)=1f(2)=1+1=2f(3)=2+1+1=4f(4)=4+2+1+1=8f(5)=8+4+2+1+1=16...归纳有:f(n)=2n−1

用2*1的小矩形横着或者竖着填充大矩形。用8个2*1的小矩形无重叠的填充2*8的大矩形,总共多少种填法?



答:

将覆盖问题记为f(8)。

当横着填时(红线),下方必须横,右边还剩f(6)

当竖着填时(蓝线),右边还剩f(7)

则f(8)=f(7)+f(6)。为斐波那契数列。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: