您的位置:首页 > Web前端

剑指Offer学习总结-斐波那契数列

2018-01-17 12:02 330 查看

剑指Offer学习总结-斐波那契数列

本系列为剑指Offer学习总结,主要是代码案例的分析和实现:

书籍链接:http://product.dangdang.com/24242724.html

原作者博客:http://zhedahht.blog.163.com/blog/static/254111742011101624433132/

原作者博客链接有完整的项目代码下载。

斐波那契数列

题目

题目:写一个函数,输入 n, 求斐波那契 ( Fibonacci ) 数列的第 n 项。

斐波那契数列定义如下:(原书的写法少了n==2的情况)

f(n)=⎧⎩⎨⎪⎪0,1,f(n−1)+f(n−2),n=0n=1n>=2

最常见的解法:

最常见的解法是递归的解法,递归的思路的关键

1.找到递归的边界条件

2.找到问题缩减的方向

3.找到前后之间的递推公式

这几条上边的公式都给的很明确了。我们可以动手来写代码。

long long Fibonacci_Solution1(unsigned int n)
{
//边界条件
if(n <= 0)
return 0;
if(n == 1)
return 1;
//递推公式 以及规模缩减
return Fibonacci_Solution1(n - 1) + Fibonacci_Solution1(n - 2);
}


简单分析一下,这种递归的解法是最简单直接的。但是可以发现计算的过程中,我们会有大量的重复计算,浪费了性能。

下图假设用参数10来举例分析,可以看到有很多重复的子问题计算。



改进的解法:

如何避免重复计算已经计算过的结果,我们可以采用两个变量来保存之前计算过的结果,f(n-1)和f(n-2).

具体需要多少个变量来存储迭代,需要具体分析具体分析,通常我们需要看递推公式来判断。

接下来我们需要将递归转换为循环,请记住二者的转换并不一定需要借助栈来进行。

判断是够需要利用栈,首先判断是否递归需要进行回溯操作。

具体关于递归和循环的转换,我们以后可以在进行讨论分析。

从下往上计算, 首先根据f(0)和 f(1)算出 f(2),再根据f(l)和 f(2)算出 f(3)……

依此类推就可以算出第 n 项了。 很容易理解, 这种思路的时间复杂度是O(n)

long long Fibonacci(unsigned n)
{
int result[2] = {0 , 1};
if(n < 2)
return result
;

long long fibMinusOne = 1;//n-1
long long fibMinusTwo = 0;//n-2
for(unsigned int i = 2 ; i <= n ; ++i)
{
fibN = fibMinusOne + fibMinusTwo;

fibMinusTwo = fibMinusOne;
fibMinusOne = fibN;
}
return fibN;
}


类似的问题

其他类似的问题,我们还有青蛙跳台阶问题,人爬梯子问题,2X1格子覆盖2Xn格子的问题。

接下来我们一一分析

青蛙跳台阶问题,一只青蛙一次可以跳上 1 级台阶, 也可以跳上 2 级 ,求 该 靑蛙跳上一个 n 级的台阶总共有多少种跳法

首先考虑最简单的情况,

1级台阶,1种跳法

2级台阶,2种跳法

再来讨论一般情况

我们把 n 级台阶时的跳法看成是 n 的函数,记为 f(n)。

当 n>2 时, 第一次跳的时候就有两种不同的选择:

一是第一次只跳 1 级, 此时跳法数目等于后面剩下的 n-1 级台阶的跳法数0, 即为f(n-1) 注意子问题分解的结果合并是乘法不是加法

另外一种选择是第一次跳 2 级, 此时跳法数目等于后面剩下的 n-2 级台阶的跳法数目, 即为 f(n-2),

因此 n 级台阶的+同跳法的总数 f(n)=f(n-1)+f(n-2),可以看出这是一个明显的斐波那契数列,

但是终止条件稍有区别。终止条件是n==1和n==2。或者n==0的时候,f(0)=0

人爬梯子的问题和青蛙跳台阶一样,就不做过多的重复了。分析问题的时候,想一想思路分析的过程中,有没有重复的情况。

2X1格子覆盖2Xn格子的问题

我们可以用 2x1的小矩形横着或者竖着去覆盖更大的矩形,请问用 8 个 2X1 的小矩形无重叠地覆盖个 2X8的大矩形, 总共有多少种方法?



我们先把以8 的覆盖方法记为 f(8)。 用第一个 2X1 小矩形去覆盖大矩形的最左边时有两个选择, 竖着放或者横着放。

当竖着放的时候, 右边还剩下 2X7 的区域, 这种情形下的覆盖方法记为 f(7)。

接下来考虑横着放的情况。

当1x2小矩形横着放在左上角的时候, 左下角必须和横着放个 1x2 的小矩形, 而在右边还还剩下 2x6 的区域, 这种情形下的覆盖方法记为 f(6),

因此 f(8)= f(7)+f(6) 此时我们可以看出, 这仍然是斐波那契数列。

但是终止条件稍有区别。

当覆盖的格子只有2X1,有一种办法

当覆盖的格子有2X2,有二种办法

public class Solution {
public int RectCover(int target) {
if(target==0){
return 0;
}
if (target == 1) {
return 1;
}
if (target == 2) {
return 2;
}
return RectCover(target - 1) + RectCover(target - 2);

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: