斐波那契数列
答案:写一个函数,输入 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:
输入:n = 2
输出:1
示例 2:
输入:n = 5
输出:5
提示:
0 <= n <= 100
解题思路
看到斐波那契数列我们最先想到的是用递归的方式,例如下面代码就是利用递归的方式实现的:
public int fib(int n) {
if(n==1 || n==2) {
return 1;
} else {
return fib(n-1) + fib(n-2);
};
其实面试遇到这种题最好不要在面试官面前用递归的方式来解题,这估计会掉入面试官挖的坑。这种做法空间时间都很不理想,它会进行很多的重复性运算。递归的好处算是显而易见了,因为符合程序设计语言和人类思维,所以递归的代码写出来都是非常简洁的,无非就是设计好终止条件,然后根据不同的条件做递归就行了。在很多情况下都是非常好用的,比如在操作数据结构中的树,比如需要使用栈的一些特性中。但是递归也有自己的缺点,首先就是开销问题,因为使用递归要多次的调用函数,这是有开销的,当问题的规模非常大的时候,过于多次的调用会使得程序的栈溢出,这也是务必需要规避的。此外,递归还有一个缺点就是有可能带来重复计算的问题。斐波那契数列的递归计算路径如下图所示:
我们一般要考虑使用其他方法来解题。
1.方法一
代码如下:
class Solution {
public:
int fib(int n) {
int constant = 1000000007;
int first = 0;
int second = 1;
while (n-- > 0) {
int temp = first + second;
first = second % constant;
second = temp % constant;
}
return first;
}
};
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:6 MB, 在所有 C++ 提交中击败了49.10%的用户
2.方法二
动态规划解析: 状态定义:设 dp为一维数组,其中 dp[i] 的值代表 斐波那契数列第 i个数字 。 转移方程:dp[i + 1] = dp[i] + dp[i - 1],即对应数列定义 f(n + 1) = f(n) + f(n - 1) ; 初始状态: dp[0] = 0, dp[1] = 1,即初始化前两个数字; 返回值: dp[n] ,即斐波那契数列的第 n 个数字。代码如下:
class Solution {
public:
int fib(int n) {
int dp[101];//0<=n<=100
dp[0]=0; dp[1]=1;
for(int i=2;i<=n;i++)
dp[i]=(dp[i-1]+dp[i-2])%1000000007;
return dp[n];
}
};
注意
方法二中数组最好定义成dp[101],而不是dp[n+1];因为这可能取决编译器版本,c++/c现在可以把数组长度定义为变量,不过里面的值是随机的,不能把数组长度定义为变量的同时进行初始化,必须在另外初始化为0。
在解题过程中对于为什么可以边算边取模和最后再取模结果一样感到困惑。不过有大佬给出了证明过程,这里粘过来给大家参考一下。
证明: 要证(a+b)%c = (a%c+b%c)%c ,即证a+b与a%c+b%c对c同余 ,则有c能整除(a+b-a%c-b%c) 。设a=mc+p、 b=nc+q ,则(a+b-a%c-b%c)=(m+n)c+p+q-p-q=(m+n)c ,则证a+b与a%c+b%c对c同余,证毕。