LeetCode 面试题 08.09. 括号
题目描述

题意分析
生成恰好
n对括号的全部合法字符串,结果不能重复。合法不仅要求左右括号总数相同,还要求从左向右读到任何位置时,右括号都不能比左括号多,因为每个右括号都要匹配此前出现的左括号。
解法:只扩展可能合法的括号前缀
核心思路
[!blue]
定义
dfs(l, r, t):已经放入l个左括号、r个右括号,形成前缀t。每步只追加一种括号,并增加对应计数,最终分别用完n个左右括号。若
l > n或r > n,数量已经超出要求;若r > l,当前前缀已经出现没有左括号可匹配的右括号。后续只能在末尾追加,无法在这个右括号之前补入左括号,所以这三种情况都可以立即剪枝。当
l == n && r == n时,总数量已满,而且之前每个前缀都通过了检查,因此当前串合法,可以直接加入答案。其他合法前缀继续尝试追加左括号和右括号;代码由下一次递归入口统一排除不合法的尝试。任意合法完整串的所有前缀都满足这些限制,所以它对应的逐字符选择不会被剪掉;不同选择路径又必然在某一位字符不同,因此结果不重不漏。字符串拼接产生新值,父层的
t不会被子层改动,不需要手动删除末尾字符。
解题步骤
- 从
dfs(0, 0, "")开始。- 进入递归时先检查数量是否超过
n,以及右括号是否多于左括号,不满足就返回。- 左右括号都已用完时,保存当前字符串并返回。
- 分别递归到追加左括号和追加右括号的状态。
- 所有可行前缀搜索结束后返回答案。若
n = 0,空字符串本身就是唯一完整结果。
代码实现
class Solution {
private List<String> answer = new ArrayList<>();
private int n;
public List<String> generateParenthesis(int n) {
this.n = n;
dfs(0, 0, "");
return answer;
}
private void dfs(int l, int r, String t) {
// l < r 表示右括号已经反超,该前缀无论怎么补都不可能合法。
if (l > n || r > n || l < r) {
return;
}
if (l == n && r == n) {
answer.add(t);
return;
}
dfs(l + 1, r, t + "(");
dfs(l, r + 1, t + ")");
}
}
func generateParenthesis(n int) []string {
answer := []string{}
var dfs func(int, int, string)
dfs = func(l, r int, t string) {
// l < r 表示右括号已经反超,该前缀无论怎么补都不可能合法。
if l > n || r > n || l < r {
return
}
if l == n && r == n {
answer = append(answer, t)
return
}
dfs(l+1, r, t+"(")
dfs(l, r+1, t+")")
}
dfs(0, 0, "")
return answer
}
复杂度分析
- 时间复杂度:设合法结果数为第 $n$ 个卡特兰数 $C_n$,输出本身需要 $\Theta(nC_n)$ 个字符。每个合法前缀都属于至少一个结果,按每个结果至多 $2n$ 个前缀计,搜索节点数有 $O(nC_n)$ 上界;当前每次字符串拼接还需复制至多 $O(n)$ 个字符,因此可给出包含复制成本的保守上界 $O(n^2C_n)$。
- 空间复杂度:不计输出时为 $O(n^2)$。递归深度是 $O(n)$,但路径上的不同层同时持有逐渐变长的字符串,长度之和为平方级;输出另外占 $O(nC_n)$。
关键点总结
[!green]
- 数量不超限和每个前缀右括号不反超,共同保证完整结果合法。
- 非法前缀无法靠后缀修复,剪枝只删除不可能产生答案的分支。
- 代码使用不可变字符串传递状态,无需撤销,但要计入拼接的复制成本。
易错点总结
[!yellow]
- 只在最后检查左右总数相同,会接受中途已经无法匹配的前缀。
- 不能只限制总长度,左右两类括号各自都不能超过
n。- 当前实现是带剪枝的回溯,计数状态并不意味着使用了动态规划或记忆化。
- 不能仅按递归层数将辅助空间记成 $O(n)$,各层字符串也同时占用空间。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 20. 有效的括号 | 简单 | 原题验证一个给定括号串,本题在构造阶段就维护单种括号的合法前缀条件。 |
| 301. 删除无效的括号 | 困难 | 同样利用左右括号余额排除非法前缀,原题还要在给定字符串上最少删除。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!