开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情
作者: 千石
支持:点赞、收藏、评论
欢迎各位在评论区交流
前言
本文内容来自我平时学习的一些积累,如有错误,还请指正
在题目实战部分,我将代码实现和代码解释设置在了解题思路的下方,方便各位作为参考刷题
本文大纲
一些话
本文内容来自我平时学习的一些积累,如有错误,还请指正
在题目实战部分,我将代码实现和代码解释设置在了解题思路的下方,方便各位作为参考刷题
题目练习步骤:
- 给自己10分钟,读题并思考解题思路
- 有了思路以后开始写代码,如果在上一步骤中没有思路则停止思考并且看该题题解
- 在看懂题解(暂时没看懂也没关系)的思路后,背诵默写题解,直至能熟练写出来
- 隔一段时间,再次尝试写这道题目
前置知识
-
实现:递归需要满足两个条件:
- 一个终止条件:防止递归无限循环
- 一个调用自身的语句:递归处理问题的子问题
-
特性:
- 易于实现和理解:对于解决的问题的某些类型,递归可以比迭代更易于理解和实现。
- 易于维护:递归代码通常比迭代代码更容易维护。
- 效率低:递归代码比迭代代码执行得慢得多,因为它需要记录递归调用的状态。
-
思维要点:
- 清晰的定义终止条件:避免无限递归。
- 确定如何将问题分解为子问题:确定如何递归地处理问题。
- 正确处理递归状态:在每次递归调用之间正确地存储和恢复状态。
题目
70. 爬楼梯
思路1:递归
这道题递归思路很明显,就是计算爬到第n阶的方案数,实际上就是前n-1阶和n-2阶的方案数之和。所以,我们可以递归地求解这两个子问题,最后相加得到答案。
复杂度分析:
递归的时间复杂度为O(2^n),因为每次递归会产生两个子问题,总的问题数是指数级增长的。但是可以通过使用缓存来优化,以避免重复计算。
代码实现:
class Solution:
@cache
def climbStairs(self, n: int) -> int:
# 定义递归终止条件:当n<=2时,直接返回n,代表有n种方法
if n <= 2:
return n
# 否则,递归地求解问题的两个子问题,即爬1阶和2阶的方案数,最后相加
else:
return self.climbStairs(n-1) + self.climbStairs(n-2)
优化:记忆化递归
使用递归来模拟爬楼梯的过程,从 n 开始递归,每次可以爬 1 阶或 2 阶,直到爬到楼顶。 使用记忆化数组 memo 来保存已经计算过的状态的结果,避免重复计算,优化时间复杂度。 复杂度分析:
复杂度分析:
时间复杂度:O(n),因为每个状态最多被计算一次,所以总的计算次数不超过 n。 空间复杂度:O(n),因为需要使用长度为 n 的数组来保存状态的结果。
代码实现
class Solution:
def climbStairs(self, n: int) -> int:
memo = [-1 for i in range(n + 1)] # 记忆化数组,初始化为-1,表示该状态没有被计算过
return self.helper(n, memo)
def helper(self, n, memo):
if n < 0: # 如果n小于0,说明该状态不合法,直接返回0
return 0
if n == 0: # 如果n为0,说明到达了楼顶,直接返回1
return 1
if memo[n] != -1: # 如果该状态已经被计算过了,直接返回记忆的结果
return memo[n]
memo[n] = self.helper(n - 1, memo) + self.helper(n - 2, memo) # 记忆该状态的结果
return memo[n]
22. 括号生成
rubyCopy code
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
res = []
self.helper(n, n, "", res) # n代表左括号个数,n代表右括号个数
return res
def helper(self, left, right, path, res):
if left == 0 and right == 0: # 如果左右括号都用完了,说明生成了一个合法的括号组合,将其加入结果列表
res.append(path)
return
if left > 0: # 如果还有左括号未用,可以放一个左括号
self.helper(left - 1, right, path + "(", res)
if right > left: # 如果还有右括号未用且剩余的右括号数大于左括号数,可以放一个右括号
self.helper(left, right - 1, path + ")", res)
思路:
- 使用递归来模拟生成括号的过程,从 n 和 n 开始递归,分别代表剩余的左括号个数和右括号个数。
- 每次递归中,可以选择放一个左括号或者右括号,并将其加入生成的括号组合字符串 path 中。
- 当左右括号都用完了,说明生成了一个合法的括号组合,将其加入结果列表。
复杂度分析:
-
时间复杂度:,每个括号有两种选择,共有 个选择,但是大部分选择是不合法的,因此实际时间复杂度接近 ,每次递归都需要生成两个结果,共有 个递归,但是因为结果的单调性,右括号的个数最多只能比左括号多一个,因此实际复杂度接近 。
-
空间复杂度:,递归栈的深度最多为 n。
代码实现:
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
res = []
self.helper(n, n, "", res) # n代表左括号个数,n代表右括号个数
return res
def helper(self, left, right, path, res):
if left == 0 and right == 0: # 如果左右括号都用完了,说明生成了一个合法的括号组合,将其加入结果列表
res.append(path)
return
if left > 0: # 如果还有左括号未用,可以放一个左括号
self.helper(left - 1, right, path + "(", res)
if right > left: # 如果还有右括号未用且剩余的右括号数大于左括号数,可以放一个右括号
self.helper(left, right - 1, path + ")", res)
总结
本文介绍了递归的实现、特性和思维要点,并且讲解了两道题目,希望对大家有所帮助