LeetCode LCR 085. 括号生成
题目描述

题意分析
用
n个左括号和n个右括号生成所有有效括号串。有效串必须满足:任意前缀中的左括号数不少于右括号数,最终两种括号都恰好使用n个。如果一个前缀已经出现右括号比左括号多的情况,后面再补字符也无法改变这个错误前缀。因此可以从空串开始,只追加仍可能组成有效串的括号,避免先枚举全部字符串再验证。
解法:只扩展合法括号前缀
核心思路
[!blue]
left、right分别表示当前前缀t中已使用的左、右括号数,始终保持0 <= right <= left <= n。left-right就是尚未闭合的左括号数,下一步只需按以下两个条件选择:
left < n时还可以添加左括号,既不会超出数量上限,也不会破坏前缀合法性。right < left时才可以添加右括号,因为必须先有一个未闭合的左括号与它配对;仅检查right < n不足以保证这一点。满足这些条件的前缀总能补成完整答案:先补足剩余左括号,再用剩余右括号逐一闭合即可。每次递归增加一个字符,因此最多深入
2n层;当left == right == n时,所有前缀都合法且左右总数配平,直接收集t。任意合法答案的每一步都符合上述选择条件,所以不会被剪掉;两个不同的左右括号选择序列也不可能得到同一个字符串,所以无需额外去重。虽然计数决定了下一步的合法选择,不同前缀即使计数相同也不能合并,否则会漏掉不同答案。
当前代码用
t + "("或t + ")"创建新字符串,父层的t不会被修改,返回后无需显式撤销。两种选择可以同时合法,因此用两个独立的if分别展开。
解题步骤
- 从
dfs(0, 0, "")开始,左右计数都为零。- 两种括号都已用满时,将当前字符串加入答案并返回。
- 左括号还未用满时,追加左括号并递归。
- 仍有未闭合左括号时,追加右括号并递归。
代码实现
class Solution {
public List<String> generateParenthesis(int n) {
List<String> answer = new ArrayList<>();
dfs(0, 0, n, "", answer);
return answer;
}
// left / right:已使用的左、右括号数量;t:当前前缀。
private void dfs(int left, int right, int n, String t, List<String> answer) {
if (left == n && right == n) {
// 不变量保证走到这里的 t 一定合法,无需再校验。
answer.add(t);
return;
}
if (left < n) {
dfs(left + 1, right, n, t + "(", answer);
}
// 必须是 right < left,写成 right < n 会放行 ")(" 这类非法前缀。
if (right < left) {
dfs(left, right + 1, n, t + ")", answer);
}
}
}
func generateParenthesis(n int) []string {
var answer []string
// left / right:已使用的左、右括号数量;t:当前前缀。
var dfs func(left, right int, t string)
dfs = func(left, right int, t string) {
if left == n && right == n {
// 不变量保证走到这里的 t 一定合法,无需再校验。
answer = append(answer, t)
return
}
if left < n {
dfs(left+1, right, t+"(")
}
// 必须是 right < left,写成 right < n 会放行 ")(" 这类非法前缀。
if right < left {
dfs(left, right+1, t+")")
}
}
dfs(0, 0, "")
return answer
}
复杂度分析
- 时间复杂度:$O(nC_n)$,C_n 为第 n 个卡特兰数:合法前缀搜索规模与 C_n 同阶,每次字符串拼接至多复制 O(n) 个字符。
- 空间复杂度:当前各层保留独立前缀串,活跃字符总空间最坏 $O(n^2)$,递归帧为 $O(n)$,输出另占 $O(nC_n)$。
关键点总结
[!green]
- 数量限制与前缀限制分别由
left < n、right < left保证,最终才得到恰好n对括号。- 只生成合法前缀,完整结果不用再校验;不同选择序列对应不同答案,也不用集合去重。
- 当前字符串不可变,递归拼接不会改变父层路径;各层仍会保留独立的前缀,空间不能只按递归帧数估算。
易错点总结
[!yellow]
- 任何前缀都需右括号数不超过左括号数,右括号的条件是 right<left。
- 左括号总数不能超过 n,只有两种括号都用满时才收集完整结果。
- 当前字符串拼接写法无需回退共享路径;若换成可变缓冲区,则必须在回溯时撤销。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 20. 有效的括号 | 简单 | 原题验证给定括号串,本题构造时就维护左括号数不小于右括号数。 |
| 301. 删除无效的括号 | 困难 | 同样利用括号余额剪枝,原题从既有字符串中最少删除,本题从空串生成。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!