题解一:记忆化搜索
假设有n阶台阶,在所有的跳法中,由于青蛙一次只能跳1步或者2步,所以青蛙跳上最后一阶只能由f(n-1)+1 或者f(n-2)+2 这两种情况得来。
即:f(n)=f(n-1)+f(n-2)
同理:f(n-1)=f(n-2)+f(n-3),以此类推。
这其实就是一个斐波拉契数列。
图示如下:
根据思路我们可以实现递归代码:
class Solution { public: int jumpFloor(int number) { if(number<=1) return 1; return jumpFloor(number-1)+jumpFloor(number-2); } };
仔细观察上图,可以发现,f(2)计算了两次,f(1)计算了3次,f(0)计算了2次。可以采用一个map存储已经被计算过的值(不采用vector的原因,不确定台阶的数量,无法预先申请确定大小的数组)。
具体实现如下:
class Solution { public: map<int,int> map_flag;//记录已经被计算出来的值 int jumpFloor(int number) { if(number<=1) return 1;//初始两个台阶的结果 if(map_flag.count(number)){//已经被计算过;采用count的函数可以降低内存与时间; return map_flag[number]; } return map_flag[number]=jumpFloor(number-1)+jumpFloor(number-2);//未被计算过,存入map; } };
复杂度分析:
时间复杂度:O(n)
空间复杂度:O(n)
题解二:动态规划
将记忆化搜索的递归部分修改为迭代。
1、题解一分析出本题f(n)可以拆分出重叠子问题f(n-1)、f(n-2);
2、f(n)=f(n-1)+f(n-2)是动态规划的状态转移方程;
3、f(0)=1,f(1)=1是动态规划的初始状态;
4、dp为一维数组,其中dp[i]的值代表青蛙跳第n个台阶的方法数;
复杂度分析:
时间复杂度:O(n)
空间复杂度:O(n),建立一个保存状态的数组;
实现如下:
class Solution { public: int jumpFloor(int number) { vector<int> dp(number+1, 0); dp[0] = dp[1] = 1;//初始状态 for (int i=2; i<=number; ++i) { dp[i] = dp[i-1] + dp[i-2];//状态转移方程 } return dp[number]; } };
动态规划优化:状态压缩
我们可以看出:
1、在计算f(3)之后的值时,f(0)再也用不上了,没有必要保存;
2、每一个状态计算直接相关的只有当前值的前2个值;
那么我们可以只用两个值做保存前两个状态:
复杂度分析:
时间复杂度:O(n)
空间复杂度:O(1)
实现如下:
class Solution { public: int jumpFloor(int number) { if(number<=1)return 1;//初始两个台阶的结果 int l1=1,l2=1;//用来保留前两次台阶的方法数 for(int i=2;i<=number;++i){ int temp=l1+l2; l1=l2; l2=temp; } return l2; } };