1、问题描述
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
提示:
1 <= n <= 45
2、解题思路
解法 1:递归(暴力解法)
思路
-
递推关系:
f(n) = f(n-1) + f(n-2)
,即爬到第n
阶的方法数等于爬到n-1
阶(最后一步迈 1 阶)和n-2
阶(最后一步迈 2 阶)的方法数之和。 -
边界条件:
-
f(1) = 1
(只有 1 阶,1 种方法) -
f(2) = 2
(可以 1+1 或直接迈 2 阶)
-
public int climbStairs(int n) {
//递归算法--->时间复杂度为O(N*N)
p(n) = P(n-1) + p(n-2)
if(n == 1){
return 1;
}
if(n == 2){
return 2;//要么一阶一阶爬,要么直接两阶
}
return climbStairs(n-1) + climbStairs(n-2);
}
复杂度分析
-
时间复杂度:
O(2^n)
,因为递归树呈指数级增长,存在大量重复计算。 -
空间复杂度:
O(n)
,递归栈深度最大为n
。
缺点
-
重复计算:比如计算
f(5)
时,f(3)
会被计算多次,效率极低。
解法 2:递归 + 记忆化(HashMap 存储)
思路
-
优化递归:使用
HashMap
存储已经计算过的f(n)
,避免重复计算。 -
步骤:
-
检查
HashMap
是否已经存储了f(n)
,如果有,直接返回。 -
如果没有,计算
f(n) = f(n-1) + f(n-2)
,并存入HashMap
。
-
private Map<Integer,Integer> storeMap = new HashMap<>();
public int climbStairs(int n) {
//使用hashmap存储计算好的第i个方法,时间复杂度为O(N),空间复杂度为O(N)
if(n==1) return 1;
if(n==2) return 2;
if(storeMap.get(n) != null){
return storeMap.get(n);
}
else{
int result = climbStairs(n-1) + climbStairs(n-2);
storeMap.put(n,result);
return result;
}
}
复杂度分析
-
时间复杂度:
O(n)
,每个f(n)
只计算一次。 -
空间复杂度:
O(n)
,存储HashMap
和递归栈。
优点
-
避免重复计算,大幅提升效率。
解法 3:动态规划(数组存储)
思路
-
递推关系:
dp[i] = dp[i-1] + dp[i-2]
,其中dp[i]
表示爬到第i
阶的方法数。 -
边界条件:
-
dp[0] = 1
(0 阶可以视为 1 种方法,方便计算) -
dp[1] = 1
(1 阶只有 1 种方法) -
dp[2] = 2
(2 阶有 2 种方法)
-
public int climbStairs(int n) {
//动态规划--->时间复杂度为O(N),空间复杂度为O(N)
if(n<=1) return 1; // 处理0和1的情况,否则后面会出现数组越界问题。
int[] dp = new int[n];
dp[0] = 1; //表示一个台阶,1种解法
dp[1] = 2; //表示两个台阶,有一步一步和直接两步的2种解法
for(int i=2;i<n;i++){
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n-1];
}
复杂度分析
-
时间复杂度:
O(n)
,只需遍历一次。 -
空间复杂度:
O(n)
,需要dp
数组存储中间结果。
优点
-
避免递归栈溢出,适合较大的
n
。
解法 4:动态规划(优化空间,仅存储前两个状态)
思路
-
优化空间:由于
dp[i]
只依赖dp[i-1]
和dp[i-2]
,可以用变量代替数组。 -
变量定义:
-
prev1
表示dp[i-1]
-
prev2
表示dp[i-2]
-
current
表示dp[i]
-
public int climbStairs(int n) {
//动态规划,不适用数组存储,因为题目要求只需要返回第n阶有多少种方式,即返回最后一次的即可
//时间复杂度为O(N),空间复杂度为O(1)
int first = 0, second = 0;
int result = 1;
for(int i=1;i<=n;i++){
first = second;
second = result;
result = first + second;
}
return result;
}
复杂度分析
-
时间复杂度:
O(n)
,遍历一次。 -
空间复杂度:
O(1)
,仅用 3 个变量。
优点
-
空间最优,适用于
n
非常大的情况。