把所有可能爬的阶数进行组合,就是1和2。
在每一步中都会继续调用climb_stairs函数模拟爬1阶和2阶的情形,并返回两个函数的返回值之和。
climb_stairs(i, n) = climb_stairs(i+1, n) + climb_stairs(i+2, n)
其中i是当前阶数,n是目标阶数。
def climbStairs(self, n: int) -> int:
"""
暴力穷举法
:param n:
:return:
"""
def clim_stairs(i, n):
if i > n:
return 0
if i == n:
return 1
return clim_stairs(i+1, n) + clim_stairs(i+2, n)
return clim_stairs(0, n)
分解示意图如下:

时间复杂度:O(2^N),它是一个树形递归,结点数量为2^N
空间复杂度:O(N),树的深度为N
上一种方法中计算每一步的结果时存在大量冗余。
改进思路是,可以把每一步的结果存储在memo数组中,当函数再次被调用,就直接从memo数组返回结果。
在memo数组的帮助下,得到了一个剪枝后的递归树,其大小减少到n。
def climbStairs_2_1(self, n: int) -> int:
"""
记忆化递归
dict 实现
时间复杂度:O(n),树形递归的大小可以达到 n。
空间复杂度:O(n),递归树的深度可以达到 n。
:param n:
:return:
"""
memo = dict()
def climb_stairs(i, n, memo):
if i > n: return 0
if i == n: return 1
if memo.get(i, 0): return memo.get(i)
memo[i] = climb_stairs(i+1, n, memo) + climb_stairs(i+2, n, memo)
return memo[i]
return climb_stairs(0, n, memo)
def climbStairs_2_2(self, n: int) -> int:
"""
记忆化递归
List 实现
:param n:
:return:
"""
memo = [0]*n
def climb_stairs(i, n, memo):
if i > n: return 0
if i == n: return 1
if memo[i]: return memo[i]
memo[i] = climb_stairs(i+1, n, memo) + climb_stairs(i+2, n, memo)
return memo[i]
return climb_stairs(0, n, memo)
输出:
执行时长:
(‘climbStairs_2_1‘, 0.0001856794491571879)
(‘climbStairs_2_2‘, 7.651112980346439e-05)
说明:
有两个版本,dict和list,奇怪的是前者的速度低,可能是由于小批量的情况下dict并不能体现查找速度方面的速度优势并不能压倒插入的开销及其它的开销。
这个问题可以分解成一些包含最优子结构的子问题。
第i阶可以由以下两种方法得到:
所以到达第i阶的方法总数就是到第(i-1)和第(i-2)阶的方法数之和
用dp[i]表示能到达第i阶的方法总数:
dp[i]=dp[i-1]+dp[i-2]
def climbStairs_dp(self, n):
"""
动态规划
时间复杂度:O(n),单循环到n。
空间复杂度:O(n),dp数组用了n的空间。
:param n:
:return:
"""
if n == 1:
return 1
dp = [0]*(n+1)
dp[1], dp[2] = 1, 2
for i in range(3, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
在动态规划解法中,可以得出dp[i]其实就是第i个斐波那契数。
所以可以使用斐波那契数的算法计算。
包括递归,迭代,矩阵,数学公式计算等。
原文:https://www.cnblogs.com/wodeboke-y/p/12375658.html