一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
1 简单粗暴的递归法
对于这种问题,只要找到递推关系即可,我们假设n级台阶一共有 f(n) 种跳法,那么这个第n个台阶可以由第 n-1 (倒数第二个)个台阶跳一次到达,也可以由第 n-2 (倒数第三个)个台阶,跳两次到达,故有:
f(n) = f(n - 1) + f(n - 2)
public class Solution {
public int jumpFloor(int target) {
if(target == 1)
return 1;
if(target == 2)
return 2;
return jumpFloor(target - 1) + jumpFloor(target - 2);
}
}
这是最简单粗暴的做法,但是同时也会存在栈崩溃的风险,因为 target 越大,递归的次数越多,最终导致无法得出结果。最主要的原因是 存在大量的重复计算 。比如你计算了
f(n) = f(n - 1) + f(n - 2)
- f(n - 1) --> = f(n - 2) + f(n - 3)
- f(n - 2) --> = f(n - 3) + f(n - 4)
这里,仅仅递归一次,就还要重复计算 f(n - 2) 和 f(n - 3) 各两次,随着递归的深入,重组计算量越来越大。
2 简单粗暴的递归法
把每次的计算结果存储起来,已经算过的表达式不再重新计算,这样可以避免上述问题。
package com.jianzhi.offer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Jump {
public static void main(String[] args) {
Solution solution = new Solution();
System.out.println(solution.jumpFloor(40));
}
static class Solution{
List<Integer> list = new ArrayList(Collections.nCopies(1000, -1));
public int jumpFloor(int target) {
if(target == 1)
return 1;
if(target == 2)
return 2;
if(list.get(target) > 0)
return list.get(target);
else {
int result = jumpFloor(target - 1) + jumpFloor(target - 2);
list.set(target, result);
return result;
}
}
}
}
3 动态规划
核心思想还是利用状态转移方程 dp[n]=dp[n-1]+dp[n-2],这种方法是从最开始的台阶逐步向上走,最大的好处也是避免了重复计算。
public class Solution {
public int JumpFloor(int target) {
if(target == 0) return 0;
if(target == 1) return 1;
if(target == 2) return 2;
int dp[] = new int[target + 1];
dp[1] = 1;
dp[2] = 2;
for(int i = 3; i <= target; i++)
{
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[target];
}
}
这题目还有其他解法,比如矩阵什么的,不是很常见,大家可自行查阅。
升级版跳台阶
现在青蛙跳到台阶n的方法升级了,可能青蛙变异了,它可以一次性跳一步,也可以一次性跳两步,甚至可以一次性就跳到n,那么跳到n又有多少种方法呢?
这个问题呢,其实也有递归和动态规划两种方法,只不过我们要考虑的是n,而不再是一次性跳1~2个台阶的问题了。
- n = 2 时,有两种跳的方式,一阶跳和两阶跳,f(2) = f(1) + f(0) = 2
- n = 3 时,有三种跳的方式,第一次跳出一阶后,后面还有f(3-1)中跳法; 第一次跳出二阶后,后面还有f(3-2)中跳法;第一次跳出三阶后,后面还有f(3-3)中跳法,f(3) = f(2) + f(1) + f(0) = 4
- 当n = n 时,第一次跳出一阶后,后面还有f(n-1)中跳法; 第一次跳出二阶后,后面还有f(n-2)中跳法…第一次跳出n阶后,后面还有 f(n-n)中跳法,即:
f(n) = f(n-1) + f(n-2) + f(n-3) + … + f(n-n) = f(0) + f(1) + f(2) + … + f(n-1)
这里其实用到了数学归纳法,又因为 f(n-1) = f(0) + f(2) + f(3) + … + f(n-2),故
两式相减得:
f(n) = 2 * f(n-1) ( n >= 2)
这里给出一个代码示意,其他方法可类比
public class Solution {
public int JumpFloor(int target) {
if(target <= 0)
return 0;
if(target == 1)
return 1;
int f = 1;
for(int i = 2; i <= n; i++){
f = 2 * f;
}
return f;
}
}