You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Note: Given n will be a positive integer.
Example 1:
Input: 2 Output: 2 Explanation: There are two ways to climb to the top. 1. 1 step + 1 step 2. 2 steps
Example 2:
Input: 3 Output: 3 Explanation: There are three ways to climb to the top. 1. 1 step + 1 step + 1 step 2. 1 step + 2 steps 3. 2 steps + 1 step
這篇博客最開始名字叫做爬梯子問題,總是有童鞋向博主反映移動端打不開這篇博客,博主覺得非常奇怪,自己也試了一下,果然打不開。心想着是不是這個博客本身有問題,於是想再開一個相同的帖子,結果還是打不開,真是見了鬼了。於是博主換了個名字,結果居然打開了?!進經過排查后發現,原來是“爬梯子”這三個字是敏感詞,放到標題里面,博客就被屏蔽了,我也真是醉了,完全是躺槍好么,無奈之下,只好改名為爬樓梯問題了 -。-|||。
這個爬梯子問題最開始看的時候沒搞懂是讓干啥的,后來看了別人的分析后,才知道實際上跟斐波那契數列非常相似,假設梯子有n層,那么如何爬到第n層呢,因為每次只能爬1或2步,那么爬到第n層的方法要么是從第 n-1 層一步上來的,要不就是從 n-2 層2步上來的,所以遞推公式非常容易的就得出了:dp[n] = dp[n-1] + dp[n-2]。 由於斐波那契額數列的求解可以用遞歸,所以博主最先嘗試了遞歸,拿到 OJ 上運行,顯示 Time Limit Exceeded,就是說運行時間超了,因為遞歸計算了很多分支,效率很低,這里需要用動態規划 (Dynamic Programming) 來提高效率,代碼如下:
C++ 解法一:
class Solution { public: int climbStairs(int n) { if (n <= 1) return 1; vector<int> dp(n); dp[0] = 1; dp[1] = 2; for (int i = 2; i < n; ++i) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp.back(); } };
Java 解法一:
public class Solution { public int climbStairs(int n) { if (n <= 1) return 1; int[] dp = new int[n]; dp[0] = 1; dp[1] = 2; for (int i = 2; i < n; ++i) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n - 1]; } }
我們可以對空間進行進一步優化,只用兩個整型變量a和b來存儲過程值,首先將 a+b 的值賦給b,然后a賦值為原來的b,所以應該賦值為 b-a 即可。這樣就模擬了上面累加的過程,而不用存儲所有的值,參見代碼如下:
C++ 解法二:
class Solution { public: int climbStairs(int n) { int a = 1, b = 1; while (n--) { b += a; a = b - a; } return a; } };
Java 解法二:
public class Solution { public int climbStairs(int n) { int a = 1, b = 1; while (n-- > 0) { b += a; a = b - a; } return a; } }
C++ 解法三:
class Solution { public: int climbStairs(int n) { vector<int> memo(n + 1); return helper(n, memo); } int helper(int n, vector<int>& memo) { if (n <= 1) return 1; if (memo[n] > 0) return memo[n]; return memo[n] = helper(n - 1, memo) + helper(n - 2, memo); } };
Java 解法三:
public class Solution { public int climbStairs(int n) { int[] memo = new int[n + 1]; return helper(n, memo); } public int helper(int n, int[] memo) { if (n <= 1) return 1; if (memo[n] > 0) return memo[n]; return memo[n] = helper(n - 1, memo) + helper(n - 2, memo); } }
C++ 解法四:
class Solution { public: int climbStairs(int n) { if(n <= 1) return 1; return climbStairs(n / 2) * climbStairs(n - n / 2) + climbStairs(n / 2 - 1) * climbStairs(n - n / 2 - 1); } };
Java 解法四:
public class Solution { public int climbStairs(int n) { if(n <= 1) return 1; return climbStairs(n / 2) * climbStairs(n - n / 2) + climbStairs(n / 2 - 1) * climbStairs(n - n / 2 - 1); } }
最后來看一種叼炸天的方法,其實斐波那契數列是可以求出通項公式的,推理的過程請參見 知乎上的這個貼子,那么有了通項公式后,直接在常數級的時間復雜度范圍內就可以求出結果了,參見代碼如下:
C++ 解法五:
class Solution { public: int climbStairs(int n) { double root5 = sqrt(5); return (1 / root5) * (pow((1 + root5) / 2, n + 1) - pow((1 - root5) / 2, n + 1)); } };
Java 解法五:
public class Solution { public int climbStairs(int n) { double root5 = Math.sqrt(5); double res = (1 / root5) * (Math.pow((1 + root5) / 2, n + 1) - Math.pow((1 - root5) / 2, n + 1)); return (int)res; } }
Github 同步地址:
https://github.com/grandyang/leetcode/issues/70
類似題目:
參考資料:
https://leetcode.com/problems/climbing-stairs/
https://leetcode.com/problems/climbing-stairs/discuss/25345/Easy-solutions-for-suggestions.
https://leetcode.com/problems/climbing-stairs/discuss/25296/3-4-short-lines-in-every-language