假设你正在爬楼梯。需要 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;
}