目录
先上题目
方法一:递归
现在让 n = 6 ,假设我们站在六楼,现在我们往楼下走,每次可以向下走一阶或两阶,那么我们走的方法就变成了第一次走一阶的总方法数加上第一次走两阶的总方法数 ,用式子表达就是 f(5) + f(4) , f(4) 和 f(5) 的解法同理。f(5) = f(4) + f(3) , f(4) = f(3) + f(2) .......
我们可以得到表达式 : f( n ) = f( n - 1 ) + f( n - 2 )。同时分析题目得到两个基本条件 :
f( 1 ) = 1 、 f( 2 ) = 2 。
所以我们可以写出递归的代码,但是!!! 注意,用递归的方法时间复杂度为 O(n^2)(原因在方法二会提到)提交上去大概率会超时,所以这个思想大家了解就好,代码和斐波那契数列的代码几乎一样。后面我们会说到更好的解法。
方法二:哈希表
提醒喔,这个方法我用Java写的。还没了解到Java的同学可以先跳过这里喔。
通过上面的图我们不难发现,每一层中都出现了重复计算过的数据,这就造成了方法一的局限性,所以我们可以用哈希表存储每次前面没有计算过的结果,当下次再需要计算的时候,先在哈希表中找一下看前面有没有计算过。
这里我们再简单介绍一下Java中 HashMap 的几个常用方法:
下面展示代码:
class Solution {
private Map<Integer,Integer> storeMap = new 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;
}
}
}
方法二的时间复杂度为 O(n)
方法三:动态规划
通过前面的分析,我们得到了方程 f( n ) = f( n - 1 ) + f( n - 2 ),同时我们去找边界条件,发现f( 0 ) = 1 、 f( 1 ) = 1.因为动态规划使用数组下标时需要从 0 开始。下面展示代码(因为C语言的动态数组比较麻烦,所以这里就用Java写了):
class Solution {
public int climbStairs(int n) {
int []dp = new int [n + 1];
dp[0] = 1;
dp[1] = 1;
for (int i = 2 ; i <= n ; ++i){
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}
时间复杂度为O(n)。
下面我们再通过滚动数组思想对上面代码进行优化:(这里用的C,因为没有用到具体的数组)
int climbStairs(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;
}
时间复杂度也是 O(n)