1. 题外话
1.1 什么叫递推式
如果一个规律(数学题、编程题),可以利用递推的思想去推导出一个公式来解决问题的话。那么这个公式就叫做递推式。
2. 题目描述
2.1 变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
3.解法1——递归
3.1 代码
public int JumpFloorII(int target) {
if (target < 2)
return target;
return JumpFloorII( target - 1) * 2; //推到递推式求出
}
3.2 分析
代码解读:
- 此种方法求出此题的递推式是关键,设有以下两个式子。
- 式子1:f(n) = f(n - 1) + f(n - 2) + ~~~ + f(0)
- 式子2:f( n - 1) = f(n - 2) + f( n - 2) + ~~~ + f(0)
- 式子1 - 式子2 得到本题的递推式——f(n) = 2*f(n - 1)
- 当然f(0) = 0,f(1) = 1
- 得到递推式后,我们可以选择利用递归的方法求出解。
复杂度解读:
- 虽然递归的次数不多,但是本身递归方法入栈出栈(线程的方法栈)也会消耗时间空间。勉强算是O(n)。
4. 解法2——算术解法
4.1 代码
public int JumpFloorII(int target) {
return (int)Math.pow(2, target - 1); //直接2的target - 1次方
}
4.2 分析
代码解读:
- 惊讶?no,因为如果我们仔细算f(n)的话,会发现0,1,2,4,8,16尼玛不就是2的次方嘛!当然f(n) = 2*f(n - 1)也提醒你了。
复杂度解读:
- 运算级别,时间O(1),不过可以再改进下。因为是2的倍数的运算,所以我们可以采用位运算。改为 return 1<<(target - 1);
- 啥?不知道位运算?建议好好复习下计算机基础。
5. 解法3——动态规划
5.1 代码
public int JumpFloorII(int target) {
if (target < 2)//不用算啦,直接返回
return target;
int[] dp = new int[target + 1];// 你想返回dp[target],你确定不多加一格?
dp[0] = 0;
for(int i = 0; i <= target; i++){
for(int j = i - 1; j > 0; j--){
dp[i] += dp[j];//dp[n]的跳法等于dp[n - 1] + dp[n - 2] + ~~~+ dp[0]种跳法
}
dp[i]++;//本身就有直接跳到n来,所以+1
}
return dp[target];
}
5.2 分析
代码解读:
- 动态规划的思想。台阶为n的解法,是台阶为n-1一直到0的台阶解法的和。所以代码中有两层循环。
复杂度解读:
- O(n的平方),很慢的一种算法。且有额外的空间消耗。
6 总结
直接运算划得来。递归竟比规划好。奈何规划失人心。