一、力扣509. 斐波那契数
题目如图:
递归法就不写了,时间复杂度为O(2^N)
动态规划解法:
首先,状态转移方程直接就可以确定:
dp[i] = dp[i - 1] + dp[i - 2]
初始化条件为:
dp[0] = 0, dp[1] = 1
因为这题下标是从0开始的,又要求第N项的斐波那契数,所以容器大小为N+1。
代码如下:
int fib (int N){
if (N <= 1) return N;
vector<int> dp[N + 1];//注意不是dp[N]
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= N; ++i){
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[N];
}
也可以想到,我们只要第N项数,并不需要整个斐波那契数列,所以我们不用把他们都存起来,因此可以进一步优化下空间复杂度:
int fib (int N){
if(N <= 1) return N;
int dp[2];//定义一个大小为2的数组就可以了
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= N; ++i){//i从0开始也可以,这里主要是设置循环的次数
int sum = dp[0] + dp[1];
dp[0] = dp[1];
dp[1] = sum;
}
return dp[1];
}
这个方法最精髓的就是for循环里的三行代码,可以这样记忆:
先用sum记录前两项的和,再依次更新dp[0]和dp[1],最后循环结束返回dp[1]。
这种方法时间复杂度为O(N),而空间复杂度相对上一方法,从O(N)降为常数,即O(1)。
二、力扣70. 爬楼梯
题目如图:
解这道题时我先写了一下前几项,
前五项分别为: 1 2 3 5 8
发现这不就是斐波那契数列吗,不过前两项分别为1和2,但是依然具有斐波那契数列的性质,即第N项等于前两项之和。
其实仔细一想,题目中为什么要说每次只能爬1个 或 2个台阶?这不就是说明,对于爬到第N层台阶来说,上一次到达的台阶就是第 N-1 和 第 N-2 两种情况!
那爬到第N层台阶的方法数不就是爬到第N-1和第N-2的方法数之和吗?于是递推关系被找出来了,从逻辑上确定了这就是斐波那契数!
代码如下:
int dimbstairs (int n) {
if (n <= 2) return n;
int dp[3];//注意是3,不是2!
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; ++i){
int sum = dp[1] + dp[2];
dp[1] = dp[2];
dp[2] = sum;
}
return dp[2];
}
这道题和上道题就一个区别,初始条件不同,题目说了,给定n是一个正整数,所以n从1开始!
但是,因为数组下标是默认以0开始的,所以写dp[1], dp[2]的时候,别忘了前面有个dp[0],因此定义数组大小时,一定是大小为3,即int dp[3],若为2则会报错!