描述
数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3 输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1 输出:["()"]
提示:
1 <= n <= 8
leecode题解灵茶山艾府
枚举填左括号还是右括号
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
ans = [] # 初始化一个空列表,用于存储最终生成的所有有效括号组合
m = n * 2 # 计算所有括号的总长度,因为一个有效的括号组合由n个左括号和n个右括号组成,所以长度为n*2
path = [''] * m # 初始化一个长度为m的空字符列表,用于构建括号组合
# 定义一个深度优先搜索(DFS)函数,用于生成括号组合
# i 表示当前填入括号的位置
# open 表示已经放入的左括号数量
# i-open 表示已经放入的右括号数量
def dfs(i: int, open: int) -> None:
if i == m: # 如果当前填入的位置已经达到总长度m,说明已经生成了一个完整的括号组合
ans.append(''.join(path)) # 将当前生成的括号组合加入结果列表
return
if open < n: # 如果左括号数量还未达到n个,可以继续放入左括号
path[i] = '(' # 在当前位置放入一个左括号
dfs(i + 1, open + 1) # 递归调用DFS,位置后移一位,左括号数量加一
if i - open < open: # 如果右括号数量少于左括号数量,可以放入右括号
path[i] = ')' # 在当前位置放入一个右括号
dfs(i + 1, open) # 递归调用DFS,位置后移一位,左括号数量不变
dfs(0, 0) # 从位置0开始,初始左括号数量为0
return ans # 返回生成的所有有效括号组合
整体分析
-
初始化数据结构:
- 创建一个空列表
ans
,用于存储所有有效的括号组合。 - 计算括号组合的总长度
m
,即n * 2
。 - 创建一个长度为
m
的空字符列表path
,用于构建括号组合。
- 创建一个空列表
-
深度优先搜索函数定义:
- 定义一个递归函数
dfs(i, open)
:i
表示当前正在填入括号的位置。open
表示当前已经放入的左括号数量。
- 当
i
等于m
时,即已经填入了m
个位置,将当前path
中的括号组合转换为字符串并加入到ans
中。
- 定义一个递归函数
-
递归生成括号组合:
- 如果
open
小于n
,可以在当前位置i
放入一个左括号'('
,递归调用dfs(i + 1, open + 1)
。 - 如果
i - open
小于open
,即右括号数量少于左括号数量,可以在当前位置i
放入一个右括号')'
,递归调用dfs(i + 1, open)
。
- 如果
-
启动递归:
- 调用
dfs(0, 0)
,从位置0开始,初始左括号数量为0,开始生成所有有效的括号组合。
- 调用
-
返回结果:
- 返回存储在
ans
列表中的所有有效括号组合。
- 返回存储在
复杂度分析
时间复杂度:分析回溯问题的时间复杂度,有一个通用公式:路径长度×搜索树的叶子数。对于本题,它等于 O(n⋅C(2n,n))。但由于左右括号的约束,实际上没有这么多叶子,根据 Catalan 数,只有 个叶子节点,所以实际的时间复杂度为 O(C(2n,n))。此外,根据阶乘的 Stirling 公式,时间复杂度也可以表示为
空间复杂度:O(n)。返回值的空间不计入。
枚举下一个左括号的位置
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
ans = [] # 用于存储所有合法的括号组合
path = [] # 用于记录当前生成的括号组合的路径
def dfs(i: int, balance: int) -> None:
# i:当前正在处理的括号的位置
# balance:当前路径中的左括号数量减去右括号数量
if len(path) == n:
# 如果路径长度等于 n,则说明当前路径已经形成了一对合法括号
s = [')'] * (n * 2) # 初始化括号数组,全用右括号
for j in path:
s[j] = "(" # 将路径中的位置设为左括号
ans.append(''.join(s)) # 形成完整的括号组合并添加到答案中
return # 结束当前递归
# 遍历可能的右括号位置
for close in range(balance + 1):
path.append(i + close) # 将当前的括号位置添加到路径中
dfs(i + close + 1, balance - close + 1) # 递归调用,更新位置和余额
path.pop() # 撤销当前位置,回溯到上一个状态
dfs(0, 0) # 初始调用
return ans # 返回所有合法括号组合
整体分析
-
初始化:创建一个列表
ans
来存储所有合法的括号组合,同时创建一个空列表path
用于记录当前生成的括号路径。 -
定义递归函数:
- 参数:函数
dfs
接受两个参数:i
表示当前处理的位置,balance
表示当前路径中左括号和右括号的差值(即平衡度)。 - 检查完成状态:如果当前路径的长度等于
n
,说明当前路径代表一个完整的合法括号组合。接着根据path
中记录的位置构建这个组合的字符串,并将其添加到结果列表ans
中。
- 参数:函数
-
生成组合:
- 遍历可能的右括号位置,根据当前的平衡度确定可能的位置。
- 将当前位置添加到
path
中,并递归调用dfs
函数来继续生成下一个位置的括号组合。 - 递归返回后,从
path
中移除当前位置,以便回溯到之前的状态,尝试其他可能的组合。
-
开始递归:从初始状态调用
dfs
函数,开始生成括号组合。 -
返回结果:所有合法的括号组合生成完成后,返回存储这些组合的列表
ans
。
Leecode题解小笨蛋
一个满二叉树,我们只需要DFS所有节点即可。先把所有组合给生成出来,即DFS所有节点。把不符合的给过滤掉结束DFS。