题目
假设你正在爬楼梯。需要 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 阶
分析和解答
方法1
我们仔细想下,实际上,可以根据第一步的走法把所有走法分为两类,第一类是第一步走了1个台阶,另一类是第一步走了2个台阶。
所以n个台阶的走法的个数就等于先走1阶后剩下的n-1个台阶的走法个数再加上先走2阶后剩下的n-2个台阶的走法个数。用公式表示就是︰
f(n) = f(n-1)+f(n-2)
这其实就是个递归公式,我们再来看下终止条件。当有一个台阶时,我们不需要再继续递归,就只有一种走法。所以f(1)=1。但是这个递归终止条件不够。
n=2时,f(2)=f(1)+f(0)。如果递归终止条件只有一个f(1)=1,那f(2)就无法求解了。所以除了f(⑴)=1这一个递归终止条件外,还要有f(0)=1,表示走0个台阶有一种走法,不过这样子有点滑稽。所以,我们可以把f(2)=2单独作为一种终止条件,表示走2个台阶,有两种走法,一步走完或者分两步来走。
所以,递归终止条件就是f(1)=1,f(2)=2。
综合在一起就是这样的:
这种方法的时间复杂度为O(n2)。
方法2
仔细分析我们上面的实现,最大的问题是什么?存在着大量的重复计算,我们以f(6)来分析一下:
可以看到在f(6)的求解过程中,f(3)和f(4)都被求解了多次,这个其实是没必要的,我们可以通过一个HashMap来保存已经求解过的f(k)。当递归调用到f(k)时,先看下是否已经求解过了。如果是,则直接从散列表中取值返回,不需要重复计算,这样就能避免刚讲的问题了。
这种方法的时间复杂度为O(n)。
方法3
在这个题目中,递归的解法是自顶向下,由最开始的顶层数字一层层分解直到最底层的f(1和f(2),再将结果又一层层累加上来。循环的解法则可以直接由底向上,从最底层的f(1和f(2),直接累加上来即可,而且从图中可以看到,上一个循环中计算出来的值刚好可以在下一个循环中使用。
不过一般来说,循环取代递归的解法在代码上要复杂一些,也比较难以理解一点。
这种方法的时间复杂度为O(n)。
package com.jiawei.recursive;
import java.util.HashMap;
import java.util.Map;
/**
* ClassName: ClimbingStairs_70
* Package: com.jiawei.recursive
* Description: (LeetCode-70) 爬楼梯
* 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
* 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
* 注意:给定 n 是一个正整数。
*
* @Author JiaWei-L
* @Create 2024/1/13 23:20
* @Version 1.0
*/
public class ClimbingStairs_70 {
public static void main(String[] args) {
ClimbingStairs_70 climbingStairs70 = new ClimbingStairs_70();
/*递归的解法*/
System.out.println(climbingStairs70.climbStairsWithRecursive(9));
/*递归的解法,用HashMap存储中间计算结果*/
System.out.println(climbingStairs70.climbStairs(9));
/*循环的解法,自底向上累加*/
System.out.println(climbingStairs70.climbStairsNoRecursive(9));
}
/*递归的解法*/
/*f(n) = f(n - 1) + f(n - 2)*/
public int climbStairsWithRecursive(int n) {
if(n == 1)
return 1;
if(n == 2)
return 2;
return climbStairsWithRecursive(n-1) + climbStairsWithRecursive(n-2);
}
private final Map<Integer,Integer> storeMap = new HashMap<>();
// private Map<Integer,Integer> storeMap = new HashMap<>();
/*递归的解法,用HashMap存储中间计算结果*/
public int climbStairs(int n) {
if(n == 1)
return 1;
if(n == 2)
return 2;
if(null != storeMap.get(n))
return storeMap.get(n);
else{
int result = climbStairs(n-1)+climbStairs(n-2);
storeMap.put(n,result);
return result;
}
}
/*循环的解法,自底向上累加*/
public int climbStairsNoRecursive(int n) {
if(n == 1)
return 1;
if(n == 2)
return 2;
int result = 0;
int pre = 2;
int prePre = 1;
for (int i = 3; i <= n; ++i) {
result = pre + prePre;
prePre = pre;
pre = result;
}
return result;
}
}