剑指 Offer 10- I. 斐波那契数列
剑指 Offer 10- I. 斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
思路1:递归
最简单的就是递归,代码也简单,需要注意的是,有结束条件
class Solution {
public int fib(int n) {
if(n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
}
代码虽然没有错,但是却超出了时间限制,显然,题目不想让我们直接使用递归
这么写的弊端是,有大量的重复计算
比如:
F(5) = F(4) + F(3)
= F(3) + F(2) + F(3)
= F(2) + F(1) + F(2) + F(2) + F(1)
= F(1) + F(0) + F(1) + F(1) + F(0) + F(1) + F(0)+ F(1)
其实,在计算F(4)的时候,就已经知道了F(3)的值,就不需要再次计算后面的F(3)了
根据题目要求,要取余,需要修改代码:
return (fib(n - 1) + fib(n - 2)) % 1000000007;
思路2:迭代
不使用递归,就一个一个加
充分利用:F(N) = F(N - 1) + F(N - 2)
其实也就是:F(N - 2) + F(N - 1) = F(N)
定义三个变量:first、second、sum
first + second = sum
下一个循环的时候,
first往后移动,就是前面的secon,也就是first = second;
second往后移动,就是前面的sum,也就是second = sum;
这样,第二次循环的first、second都有值了,就可以求出第二次的sum值了
最后,返回sum即可。
需要注意的是,first、second、sum的初始值
因为循环是从i = 2开始,那么第一个sum,就是F(0) + F(1)的结果
那么,我们可以将firset = F(0) = 0、second = F(1) = 1
sum = 0或者 sum = 1都可以,因为,后面还要将其重新赋值。
class Solution {
public int fib(int n) {
//if(n < 0) return -1;
if(n <= 1) return n;
int first = 0;
int second = 1;
int sum = 0;
//0, 1, 1, 2, 3, 5, 8,...
for(int i = 2; i <= n; i++){
sum = first + second;
sum = sum % 1000000007;
first = second;
second = sum;
}
return sum;
}
}
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
这句话没看明白,明明写对了,怎么计算结果不对?
对人生产生了怀疑,看了答案才知道,少了一个取余操作
sum = sum % 1000000007;
思路3:dp
在看答案的时候,看到有人使用dp,想想,确实这道题确实是将原问题分解为子问题求解,那么,尝试下dp解决问题:
1.是否为最值问题?
好像,,,没有明显的求最值
但是,F(n)也算是最大值
2.能否使用dp
- 原问题是否可以分解为子问题
可以 - 是否有后效性
无后效性
3.如何使用dp
-
确定dp含义
dp(n) = F(n) -
确定边界值
dp(0) = 0;
dp(1) = 1; -
确定转移方程
dp(n) = dp(n - 1) + dp(n - 2);
基于以上dp的三大问,我们尝试写下代码:
class Solution {
public int fib(int n) {
//边界条件判断
if(n < 0) return -1;
if(n <= 1) return n;
int dp[] = new int[n + 1];
dp[0] = 0;
dp[1] = 1;
for(int i = 2; i <= n; i++){
dp[i] = dp[i - 1] + dp[i - 2];
dp[i] = dp[i] % 1000000007;
}
return dp[n];
}
}
其实,说起来,迭代与dp方法很类似
dp使用的是数组
迭代定义了三个变量
可以将迭代理解为dp的优化版本,毕竟,dp使用数组这个方法是可以优化的,具体的优化方案就不说了,有兴趣的小伙伴可以自己查阅下资料。