题目描述:
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
我的第一想法是递归:
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)
但这样会超出时间限制,因为会重复计算相同的子问题,比如说计算n=5时,会先计算子问题n=4和n=3,再计算n=3、n=2(n=4的子问题)和n=2、n=1(n=3的子问题),这样就相当于n=3和n=2被重复计算了。如果我们能够把计算过的结果保存起来,这样就可以在需要用的时候直接取用,而不必重复计算了。
解法1:动态规划
动态规划(Dynamic Programming)是一种算法设计技术,它通常用于优化递归算法,避免重复计算相同子问题,从而提高算法的效率。
在动态规划中,问题被分解为更小的子问题,然后通过解决子问题来解决原始问题。与递归不同的是,动态规划将中间结果保存起来,避免重复计算,从而节省时间。
在爬楼梯问题中,每一步可以选择爬1个台阶或2个台阶,到达楼顶的方法数取决于到达前两个台阶和前一个台阶的方法数。动态规划通过填充一个数组(例如 dp
数组)来保存中间结果,从底向上计算并存储到达每个台阶的方法数。这样,每个子问题只需解决一次,而不是递归方法中的重复计算。
动态规划的“动态”体现在问题的阶段性划分和中间结果的保存,而“规划”则指的是在解决问题的阶段中进行的决策。在爬楼梯问题中,每一步决策是选择爬1个台阶或2个台阶,而中间结果则是保存在 dp
数组中。
总的来说,动态规划是通过划分问题、保存中间结果、递推求解的一种优化算法技术,它在很多问题中能够有效地提高算法的效率。
class Solution:
def climbStairs(self, n: int) -> int:
if n <= 2:
return n
dp = [0] * (n + 1) # 列表dp中的每项dp[n]保存到达第n阶台阶的方法数
dp[1] = 1
dp[2] = 2
for i in range(3, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
解法2:记忆化搜索
记忆化搜索是一种优化递归算法的技术,它通过缓存(记忆)先前计算的结果,以避免对相同的输入参数进行重复计算
class Solution:
def climbStairs(self, n: int) -> int:
memo = {}
def helper(n):
if n <= 2:
return n
if n not in memo:
memo[n] = helper(n - 1) + helper(n - 2)
return memo[n]
return helper(n)
@cache
装饰器:这是一个装饰器,用于将下面的函数 dfs
转化为记忆化搜索的版本。它会自动缓存函数的输入参数和对应的输出结果,以避免对相同参数的重复计算。
class Solution:
def climbStairs(self, n: int) -> int:
@cache
def dfs(i:int)->int:
if i<=1:
return 1
else:
return dfs(i-1) +dfs(i-2)
return dfs(n)