題目
假設你正在爬樓梯。需要 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 階
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/climbing-stairs
思路
對於這樣一個問題,我們先來思考下能否遞歸的解決,它內部是否有遞歸的結構。
我們自頂向下的思考問題,直接考慮第階臺階,並且假設它下面的階,階等子問題都已經解決了。
如果我們要想爬上階臺階,因爲一次要麼爬階,要麼爬階,所以爬階臺階有兩種可能。
從階再爬階或者從階再爬階。
不難看出這是一個遞歸問題,我們把問題轉換爲爬階有多少種方法和爬階有多少種方法。然後把這兩個問題的答案相加就好了。這樣把一個大的問題轉換爲兩個小問題。
用同樣的思路可以求出爬階和爬階的方法數。從上面這個遞歸樹可以看出,存在很多重複子問題。
下面我們先寫出按照這種思路解決問題的遞歸算法。
代碼
遞歸
class Solution:
def climbStairs(self, n: int) -> int:
# 遞歸的終止條件
if n == 1:
return 1
if n == 2:
return 2
return self.climbStairs(n-1) + self.climbStairs(n-2)
雖然思路是對的,但是遞歸的方式是比較低效的,這裏導致了計算超時。參閱LeetCode刷題之動態規劃思想,我們可以將其改成記憶化搜索的方式,解決重疊子問題。
因爲爬2階臺階有2種方法,和斐波那契數列很像。這裏的遞歸終止條件還可以寫成
if n == 0: return 1 if n == 1: return 1
這樣這個問題就是斐波那契數列的應用。
記憶化搜索
dp = {}
class Solution:
def climbStairs(self, n: int) -> int:
# 遞歸的終止條件
if n == 0:
return 1
if n == 1:
return 1
if n not in dp: # 如果沒有計算過再去計算
dp[n] = self.climbStairs(n-1) + self.climbStairs(n-2)
return dp[n]
最後改成動態規劃也就很簡單了。
動態規劃
class Solution:
def climbStairs(self, n: int) -> int:
if n == 1:
return 1
dp = [1] * (n+1) #dp[0] = 1 , dp[1] = 1 ,dp[2]以後的通過下面的式子計算
for i in range(2,n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
動態規劃不需要遞歸求解,是一種自底向上的求解思想。