剑指 Offer 10- I. 斐波那契数列
题目描述:
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(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
解法一:打表
思路:
考虑到斐波那契数列的定义,可以设置一个数组,这个数组存放着从0到n的斐波那契数列的值,由定义可知,下标为N的位置的值由下标为N - 1 和 N - 2 两个地方的值相加得到,且已知头两个位置的值,即我们可以通过一个循环,求得所需的值
时间复杂度:O(n)
空间复杂度:O(n)
代码:
class Solution {
static final int mod = 1000000007;
public int fib(int n) {
int[] ans = new int[105];
ans[0] = 0;
ans[1] = 1;
for (int i = 2; i <= n; i++){
ans[i] = (ans[i - 1] + ans[i - 2]) % mod;
}
return ans[n];
}
}
解法二:递推
思路:
考虑到解法一的局限性:要设置一个数组来存放斐波那契数列的n之前的数据的值,但题目只要求我们求得下标为n的位置上的值。因此我们可以设置两个变量,进行滚动递推,可以节省许多空间
时间复杂度:O(n)
空间复杂度:O(1)
代码:
class Solution {
static final int mod = 1000000007;
public int fib(int n) {
if (n <= 1) return n;
int a = 0, b = 1; // 赋初始值
for (int i = 1; i < n; i++){
int temp = b;
b = (a + b) % mod; // 进行相加,所得的值赋予 b
a = temp % mod; // 将相加之前 b 的值赋予 a ,从而实现a与b表示的范围向前移动一步
}
return b;
}
}
解法三:矩阵快速幂
思路:
本解法涉及到了两方面的知识,一方面为与线性代数有关的构造矩阵的方法,一方面为快速幂相关的知识
-
对于构造矩阵方面的知识:
-
快速幂相关知识
由上一个知识点,我们将问题转化成了如何快速求得一个矩阵的n - 1次方的幂,这就涉及到了快速幂
-
将两个知识结合起来,就可以快速求得斐波那契数列的第n项
时间复杂度:O(㏒₂n)
空间复杂度:O(1)
代码:
class Solution {
static final int mod = 1000000007;
public int fib(int n) {
if (n <= 1) return n;
int[][] matrix = new int[][]{{1, 1}, {1, 0}}; // 构建两个矩阵
int[][] ans = new int[][]{{1}, {0}};
int x = n - 1;
while (x > 0){ // 快速幂
if ((x & 1) != 0) ans = mul(matrix, ans); // 如果二进制表示下,该位为1,就加上底数的对应次方
matrix = mul(matrix, matrix); // 底数平方
x >>= 1; // 指数除2,即指向二进制表示下的下一位数
}
return ans[0][0] % mod;
}
// 定义一个矩阵相乘的函数
int[][] mul(int[][] a, int[][] b){
int x = a.length, y = b[0].length, z = b.length; // 一个 x*y 的矩阵和 y*z 的矩阵相乘,得到一个 x*z 的矩阵
int[][] res = new int[x][z];
for (int i = 0; i < x; i++){
for (int j = 0; j < y; j++){
for (int k = 0; k < z; k++){
res[i][j] += (int)((long)((long)a[i][k] * (long)b[k][j]) % mod);
// 由于运算过程中可能会出现超出int范围的数值,可以将其转换为long之后,经过运算和求模,再转换为int
// 如果觉得麻烦,就直接将数值全设置为long,在最后返回的时候,再转换为int即可
}
}
}
return res;
}
}