假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
解法:
参考:程序员小灰 https://mp.weixin.qq.com/s/3h9iqU4rdH3EIy5m6AzXsg
其实爬楼梯是最简单的动态规划问题,因为只有单一变量。此问题可以转为斐波那契数列来解,也即是:
状态转移方程:F(n) = F(n-1) + F(n-2)
最优子结构:F(n-1) 和 F(n-2)
边界:F(1) =1, F(2)=2
由此,分别得到以下解法以及优化解法
/*** 基础版递归解法* 由此,可以得到一个二叉树* 二叉树的节点数即是需要计算的次数* 时间复杂度O(n`2)* @param n* @return*/public int climbStairs(int n) {if(n<1) {return 0;}else if (n==1) {return 1;}else if (n==2) {return 2;}return climbStairs(n-1)+climbStairs(n-2);}
可以发现,很多值是重复计算了。比如n==5时,n=3就重复计算了两次,是浪费的。其实只需要计算O(n)次。所以,可以使用缓存,缓存之前的计算结果。降低时间复杂度。
代码如下:
public int climbStairs(int n) {if(n<1) {return 0;}else if (n==1) {return 1;}else if (n==2) {return 2;}Map<Integer, Integer> map = new HashMap<Integer, Integer>();map.put(1, 1);map.put(2, 2);return climbStairs(n, map);}public int climbStairs(int n, Map<Integer, Integer> map) {if(map.containsKey(n)) {return map.get(n);}else {int value = climbStairs(n-1, map)+climbStairs(n-2, map);map.put(n, value);return value;}}
目前空间复杂度为O(n),从这方面入手,继续优化。目前的思路是为计算F(n),依次计算F(n-1),F(n-1)…2,1 。将沙漏倒过来思考,也就是
已知:F(1),F(2),那可以算出来F(3)
已知:F(2),F(3),那可以算出来F(4)
所以,想要计算n,其实只需要记录n-1,n-2的step,就可以实现了。空间复杂度从O(n)到O(1)
代码如下:
/*** 时间复杂度O(n)* 空间复杂度O(1)* 最优解* @param n* @return*/
public int climbStairs(int n) {if(n<1) {return 0;}else if (n==1) {return 1;}else if (n==2) {return 2;}int n_1=2,n_2=1;int temp =0;for(int i=3;i<=n;i++) {temp = n_1 + n_2;n_2 = n_1;n_1 = temp;}return temp;}