描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。
数据范围: 1 ≤ n ≤ 20
进阶:空间复杂度 O(1) , 时间复杂度 O(1)
示例1
输入:3
返回值:4
示例2
输入:1
返回值:1
方法一:递归
题解思路:考虑最后一步是跳几阶到达目标位置的。
主要分析:
-
令f(n)表示n阶台阶总的跳法
-
假设最后只跳一步,那么f(n) = f(n-1); 最后跳两步,那么f(n) = f(n-2);以此类推,可知总的跳法为f(n) = f(n-1) + f(n-2) +…+f(0)
图示:
代码:
class Solution {
public:
int jumpFloorII(int number) {
int ans = 1;
for(int i = 1; i < number;i++)
ans += jumpFloorII(number-i); //计算f(number-1)
return ans;
}
};
运行时间:5ms
超过6.80% 用C++提交的代码
占用内存:560KB
超过64.33%用C++提交的代码
复杂度分析:
时间复杂度:O(N^N)
空间复杂度:O(N) : 递归栈深度
方法二:记忆化优化递归
解题思路:与递归思路一样,但是普通的递归进行了多次无用计算。 比如在上述递归中,f(1),f(2)…被多次计算。我们可以使用记忆化来优化复杂度。
代码:
class Solution {
public:
int f[10001]{0}; //记录
int jumpFloorII(int number) {
if(number == 0) return 1; //边界
if(f[number]) return f[number]; //如果f[nummber]之前已经计算过,那么直接返回
//f[number] 没有计算过,f[number] = f[number-1] +.....+f[0]
for(int i = 0 ;i < number;i++)
f[number] += jumpFloorII(i);
return f[number];
}
};
运行时间:4ms
超过13.54% 用C++提交的代码
占用内存:652KB
超过49.21%用C++提交的代码
复杂度分析:
时间复杂度:O(N),从1~n依次遍历了台阶数
空间复杂度:O(N),利用一个标记数组f,记录以往被运算过的台阶的跳法;
方法三: 动态规划+
题解思路:延续题解一中的公式f(n) = f(n-1) + f(n-2) +…+f(0)
分析:
-
知道f(n) = f(n-1) + f(n-2) +…+f(0),那么f(n-1) = f(n-2) + f(n-3) +…+f(0);
-
可知 f(n) = 2 * f(n-1);
-
初始ans = 1;
代码:
class Solution {
public:
int jumpFloorII(int number) {
if(number == 1 || number == 0) return 1;
int ans = 1;
for( int i = 2; i <= number;i++)
ans *= 2;
return ans;
}
};
运行时间:4ms
超过13.54% 用C++提交的代码
占用内存:524KB
超过70.25%用C++提交的代码
复杂度分析:
时间复杂度:O(N),从1~n依次遍历了台阶数
空间复杂度:O(1),没有申请其他空间存放数据
方法四: 数学
解题思路: 从题解三得出的公式,继续优化复杂度
分析:
f(1) = 1;
f(2) = 2 * f(1) = 2^1
f(3) = 2 * f(2) = 4 = 2^2
假设f(k) = 2^(k-1) 成立
那么 f(k+1) = 2 * f(k) = 2^(k);
得出 f(n) = 2^(n-1);
代码:
class Solution {
public:
int jumpFloorII(int number) {
if(number == 1 || number == 0) return 1;
return 1 << number - 1;
}
};
运行时间:4ms
超过13.54% 用C++提交的代码
占用内存:524KB
超过70.25%用C++提交的代码
复杂度分析:
时间复杂度:O(1),一次位运算
空间复杂度:O(1),没有使用其他空间