标签:
递归、记忆化搜索、动态规划
题目:
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
数据范围:1 \leq n \leq 401≤n≤40
要求:时间复杂度:O(n)O(n) ,空间复杂度: O(1)O(1)
反思:
解这个题目有三种方法:
一种是用递归来解决,另一种是用动态规划来解决,由于递归会损耗较大的系统资源,会存在大量的重复计算,性能非常低下(其实我们这里也可以提升递归性能,就是可以先把计算好的答案存下来,即造一个备忘录,等到下次需要的话,先去备忘录查一下,如果有,就直接取就好了,备忘录没有才开始计算,那就可以省去重新重复计算的耗时,一般使用一个数组或者一个哈希map充当这个备忘录),这里我们使用三种方法来解决这个典型的青蛙跳台阶的问题,然后我再比较一下三者的时间复杂度。
该题目具体的分下在下面的代码块中有分析,具体见下文代码块。
说明
这个问题最主要是用动态规划来解决的,使用动态规划来解决的时候,性能和效率也会得到较大的提高动态规划跟带备忘录的递归解法基本思想是一致的,都是减少重复计算,时间复杂度也都是差不多。但是呢:
1、带备忘录的递归,是从f(10)往f(1)方向延伸求解的,所以也称为自顶向下的解法。
2、动态规划从较小问题的解,由交叠性质,逐步决策出较大问题的解,它是从f(1)往f(10)方向,往上推求解,所以称为自底向上的解法。
只要掌握了递归思想和动态规划的思想,这个问题其实还是很好解决的,具体动态规划的思想见我的另一篇博客文章,这里就不再一一描述了,下面附上链接
原文链接:https://blog.csdn.net/weixin_51921447/article/details/125952610
版权声明:本文为CSDN博主「青い記憶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载
用到的知识点:
递归、缓存思想、动态规划
代码:
用递归的方法解决
public int jumpFloor(int target) {
// 跳到10台
// 需要跳到9台,然后跳1个台阶到10,需要跳到8台,然后跳2个台阶到10 共两种选择刚刚好
// 跳到9台
// 需要跳到8台,然后跳1个台阶到9,需要跳到7台,然后跳2个台阶到9 共两种选择刚刚好
// ...
// 跳到2台
//需要跳到1台,然后跳1个台阶到2,需要跳到0台,然后跳2个台阶到2 共两种选择刚刚好
// F(10) = F(9) +F(8)
// F(9) = F(8) + F(7)
// F(8) = F(7) + (6)
// ...
// F(2) = 2
// F(1) = 1
if (target < 0) {
return -1;
}
if (target == 2) {
return 2;
}
if (target == 1) {
return 1;
}
return jumpFloor(target - 1) + jumpFloor(target - 2);
牛客上提交:
用递归备忘录的方法解决(记忆化搜索)
public class Solution {
private Map<Integer, Integer> map = new HashMap();
public int jumpFloor1(int target) {
if (target < 0) {
throw new IllegalArgumentException();
}
if (target == 2) {
return 2;
}
if (target == 1) {
return 1;
}
int targetOneValue = 0;
int targetTwoValue = 0;
if (map.containsKey(target - 1)) {
targetOneValue = map.get(target - 1);
} else {
map.put(target - 1, jumpFloor1(target - 1));
}
if (map.containsKey(target - 2)) {
targetTwoValue = map.get(target - 2);
} else {
map.put(target - 2, jumpFloor1(target - 2));
}
if ((targetOneValue != 0) && (targetTwoValue != 0)) {
return targetOneValue + targetTwoValue;
}
if (targetOneValue != 0) {
return targetOneValue + jumpFloor1(target - 2);
}
if (targetTwoValue != 0) {
return jumpFloor1(target - 1) + targetTwoValue;
}
return jumpFloor1(target - 1) + jumpFloor1(target - 2);
}
牛客上提交:
用动态规划的方法来解决
public int jumpFloor2(int target) {
// 动态规划一般步骤
// 1、穷举列出
// 2、确定边界
// 3、找出规律,确定最优子结构
// 4、写出状态转移方程
// F(1) = 1 , F(2) = 2 ,
// F(3) = F(2) + F(1),
// F(4) = F(3) + F(2)
// F(5) = F(4) + F(3) .....
// 边界 F(1) = 1 , F(2) = 2
// F(n) = F(n-1) + F(n-2),且观察式子可得后一个都是前两个的和
if (target < 0) {
throw new IllegalStateException();
}
int toFloorOneMethods = 1;
int toFloorTwoMethods = 2;
int result = 0;
if (target == 1) {
return toFloorOneMethods;
}
if (target == 2) {
return toFloorTwoMethods;
}
for (int i = 3; i <= target; i++) {
result = toFloorOneMethods + toFloorTwoMethods;
toFloorOneMethods = toFloorTwoMethods;
toFloorTwoMethods = result;
}
return result;
牛客上提交:
总结
带备忘录的递归和动态规划的方法来解决这道题目的时间相差不大,但是两者比起原生的递归方法在时间上有较大的提升,故时间比较:递归>带备忘录的递归>动态规划。