前言
利用「尾递归」、「动态规划」、「递推」和「矩阵快速幂」来解决问题。
剑指Offer 10-Ⅰ.斐波那契数列
问题描述
写一个函数,输入 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。
示例
|| 输入:n = 2 || 输出:1
尾递归
一开始直接用普通递归做,n稍微大一点的时候就超出时间限制了,于是想到「尾递归」
代码
class Solution {
public:
int fib(int n) {
return fibTail(n,0,1);
}
int fibTail(int n,int curr,int next){
int mod = 1e9+7;
if(n==0)
return curr;
return fibTail(n-1,next%mod,(curr+next)%mod);
}
};
动态规划
题中已经给出了状态转移方程 F(N) = F(N - 1) + F(N - 2) 与n值对应,n是从0开始的,我们申请一个大小为 n+1 的数组 dp, 定义 dp[i] 为斐波那契数列第 i 项的值, 那么 dp[n] 为斐波那契数列第 n 项的值, dp[0]=0 和 dp[1]=1 为两个初始状态, 利用dp[i] = dp[i-1] + dp[i-2]循环计算dp[i]的值。
代码
class Solution {
public:
int fib(int n) {
if(n<=1)
return n;
int mod = 1e9+7;
int dp[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] %= mod;
}
return dp[n];
}
};
递推
根据 F(N) = F(N - 1) + F(N - 2),我们知道 F(N) 的值只与 F(N - 1) 和 F(N - 2) 有关,那就可以用递推来实现动态规划,也就不需要申请一个数组来保存过程中的值,我们只申请三个常量即可。
代码
class Solution {
public:
int fib(int n) {
if(n<=1)
return n;
int mod = 1e9+7;
int a = 0,b = 1;
int sum = a + b;
for(int i=2;i<n;++i){
a = b;
b = sum;
sum = (a + b)%mod;
}
return sum;
}
};
矩阵快速幂
本题可以根据构造合适的矩阵乘法来实现 F(N) = F(N - 1) + F(N - 2) 中 F(N)、F(N - 1) 和 F(N - 2) 之间的依赖关系,利用 F(N - 1) 和 F(N - 2) 推导出 F(N) 。 根据矩阵乘法构造出
[
F
(
n
)
F
(
n
?
1
)
]
=
[
F
(
n
?
1
)
+
F
(
n
?
2
)
F
(
n
?
1
)
]
=
[
1
1
1
0
]
×
[
F
(
n
?
1
)
F
(
n
?
2
)
]
\left[ \begin{array}{l} F(n)\\ F(n - 1) \end{array} \right] = \left[ \begin{array}{l} F(n - 1) + F(n - 2)\\ F(n - 1) \end{array} \right] = \left[ {\begin{matrix} 1&1\\ 1&0 \end{matrix}} \right] \times \left[ \begin{array}{l} F(n - 1)\\ F(n - 2) \end{array} \right]
[F(n)F(n?1)?]=[F(n?1)+F(n?2)F(n?1)?]=[11?10?]×[F(n?1)F(n?2)?] 令
m
a
t
=
[
1
1
1
0
]
mat = \left[ {\begin{matrix} 1&1\\ 1&0 \end{matrix}} \right]
mat=[11?10?] 我们已知的初始状态为
[
F
(
0
)
F
(
1
)
]
\left[ \begin{array}{l} F(0)\\ F(1) \end{array} \right]
[F(0)F(1)?]
推出
[
F
(
n
)
F
(
n
?
1
)
]
=
m
a
t
×
m
a
t
×
?
m
a
t
×
[
F
(
0
)
F
(
1
)
]
=
m
a
t
×
m
a
t
×
?
m
a
t
×
[
0
1
]
=
m
a
t
n
?
1
×
[
0
1
]
\left[ \begin{array}{l} F(n)\\ F(n - 1) \end{array} \right] = mat \times mat \times \cdots mat \times \left[ \begin{array}{l} F(0)\\ F(1) \end{array} \right] = mat \times mat \times \cdots mat \times \left[ \begin{array}{l} 0\\ 1 \end{array} \right] = ma{t^{n - 1}} \times \left[ \begin{array}{l} 0\\ 1 \end{array} \right]
[F(n)F(n?1)?]=mat×mat×?mat×[F(0)F(1)?]=mat×mat×?mat×[01?]=matn?1×[01?] 所以我们可以利用快速幂求出
m
a
t
n
?
1
ma{t^{n - 1}}
matn?1 直接循环累乘也可以求出来,但是时间复杂度为O(n),而快速幂算法每一步都把指数分成两半,用相应的底数做平方运算,所需要执行的循环次数也变小,时间复杂度为O(log n)。
代码
class Solution {
public:
int mod = 1e9+7;
vector<vector<long>> mul(vector<vector<long>>& a, vector<vector<long>>& b){
int arow = a.size(); //a的行数
int brow = b.size(); //b的行数
int bcol = b[0].size(); //b的列数
vector<vector<long>> ans(arow,vector<long>(bcol,0)); //arow行bcol列,初始化为0
for(int i=0;i<arow;++i){
for(int j=0;j<bcol;++j){
for(int k=0;k<brow;++k){
ans[i][j] += a[i][k] * b[k][j];
ans[i][j] %= mod;
}
}
}
return ans;
}
int fib(int n) {
if (n <= 1)
return n;
vector<vector<long>> mat = {{1, 1},
{1, 0}};
vector<vector<long>> ans = {{1},
{0}};
int x = n - 1;
while (x != 0) {
if ((x & 1) != 0) ans = mul(mat, ans);
mat = mul(mat, mat);
x >>= 1;
}
return ans[0][0] % mod;
}
};
微信公众号文章
剑指Offer 10-Ⅰ.斐波那契数列
|