算法 跳台阶基础版、改进版(不能连续跳2阶)
@author:Jingdai
@date:2020.11.15
跳台阶(青蛙跳)是一个非常经典的算法问题,前几天看面试题看到一个跳台阶的改进版(不能连续跳2个台阶),现总结一下。
在介绍改进版解法之前先复习一下基础版,不想看的可以直接跳到改进版部分。
基础版
题目描述
有一个 n 阶的楼梯,你从最下面往上跳,每次可以跳1阶或2阶,有多少种方法可以跳到楼顶?
思路及代码
这个基础版很简单,对于第 n 阶,你只有两种方式跳上去:
- 从 n-1 阶跳1阶跳上去
- 从 n-2 阶跳2阶跳上去
所以就可以得到递推式:
然后可以利用递归方式求解或利用dp方式求解。看下面的代码:
递归方式(不推荐,复杂度高,n较大时容易超时)
public int jumpStairs(int n) { if (n <= 3) return n; return jumpStairs(n-1) + jumpStairs(n-2); }
dp方式
public int jumpStairs(int n) { if (n <= 3) return n; int[] dp = new int[n]; for (int i = 0; i < n; i++) { if (i <= 2) { dp[i] = i+1; } else { dp[i] = dp[i-1] + dp[i-2]; } } return dp[dp.length-1]; }
同时发现 dp 时只需要最后一个n的结果,所以可以优化一下空间复杂度,得到如下的代码:
扫描二维码关注公众号,回复: 12814220 查看本文章
优化dp
public int jumpStairs(int n) { if (n <= 3) return n; int prePre = 2; int pre = 3; int cur = 5; for (int i = 4; i <= n; i++) { cur = prePre + pre; prePre = pre; pre = cur; } return cur; }
同时,你会发现这个递归式就是斐波那契数列的式子,所以也可以直接用斐波那契数列的公式直接进行计算。
改进版
题目描述
题目基本一样,有一个 n 阶的楼梯,你从最下面往上跳,每次可以跳1阶或2阶,但是不能连续跳两次2阶,有多少种方法可以跳到楼顶?
思路及代码
利用之前基础版的思路,对于第 n 阶,你只有两种方式跳上去:
- 从 n-1 阶跳1阶跳上去
- 从 n-2 阶跳2阶跳上去(同时 n-2 阶必须是跳 1 阶跳上去的)
这里多了一个限制:从 n-2 阶跳 2 阶跳到 n 阶的 n-2 阶状态必须是从 n-3 阶跳1阶跳到 n-2 阶的。所以,这里我们动态规划不仅要保存跳到第 i 阶的所有方法数,还要保存最后一步通过跳 1 阶跳到第 i 阶的方法数。这里用
f(n)
表示跳到第 n 阶的总方法数,用g(n)
表示最后一步跳一阶跳到第 n 阶的方法数,这样就得到递推式:根据此就可以写代码了,看下面的代码片段。
public int modifiedJumpStairs(int n) { if (n <= 3) return n; // first total methods number // second last step is one step methods number int[][] dp = new int[n][2]; dp[0][0] = 1; dp[0][1] = 1; dp[1][0] = 2; dp[1][1] = 1; dp[2][0] = 3; dp[2][1] = 2; for (int i = 3; i < n; i++) { dp[i][0] = dp[i-1][0] + dp[i-2][1]; dp[i][1] = dp[i-1][0]; } return dp[dp.length-1][0]; }
我们再细心观察上面的递推式。
如图,递推式进行代换后可以更加简单。根据推出的结论,可以将二维数组改成一维数组,看下面的代码片段。
public int modifiedJumpStairs(int n) { if (n <= 3) return n; int[] dp = new int[n]; for (int i = 0; i < n; i++) { if (i <= 2) { dp[i] = i+1; } else { dp[i] = dp[i-1] + dp[i-3]; } } return dp[dp.length-1]; }
当然,上面代码还可以优化,因为只需要最后一个为 n 的结果,前面的都不需要,故可以把空间复杂度优化为
O(1)
,如下代码。public int modifiedJumpStairs(int n) { if (n <= 3) return n; int prePrePre = 1; int prePre = 2; int pre = 3; int cur = 4; for (int i = 4; i <= n; i++) { cur = pre + prePrePre; prePrePre = prePre; prePre = pre; pre = cur; } return cur; }