回溯生成括号组合,open < n 放左括号,close < open 放右括号,保证前缀合法。
OJ: leetcodecn
题目 ID: generate-parentheses
难度:普及+/提高
标签:回溯枚举递归字符串
日期: 2026-07-29 11:25
题意
给定整数
合法括号组合要求:任意前缀中左括号数量
思路
最直接的思路是枚举所有长度为
cpp
// brute.cpp:小数据暴力解,01 序列递归枚举所有括号序列,叶子节点检查合法性。
#include <bits/stdc++.h>
using namespace std;
int n;
int choose[20]; // 0 = '(' , 1 = ')'
vector<string> ans;
bool check() {
int bal = 0;
for (int i = 1; i <= 2 * n; i++) {
if (choose[i] == 0)
bal++;
else
bal--;
if (bal < 0)
return false;
}
return bal == 0;
}
void dfs(int dep) {
if (dep == 2 * n + 1) {
if (check()) {
string s;
for (int i = 1; i <= 2 * n; i++)
s += (choose[i] == 0 ? '(' : ')');
ans.push_back(s);
}
return;
}
choose[dep] = 0;
dfs(dep + 1);
choose[dep] = 1;
dfs(dep + 1);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
dfs(1);
for (auto &s : ans)
cout << s << '\n';
return 0;
}这个暴力先生成完整序列再到叶子节点检查合法性。它在小数据上可靠,但分支数为
优化的关键是剪枝:不等到序列完成后再检查,而是在每一步就保证前缀合法。
open < n时可以放左括号——左括号总数不能超过。 close < open时可以放右括号——任意前缀中右括号不能多于左括号。
这两个条件保证每一步之后前缀仍然合法,因此最终到达叶子时一定是一个合法组合,且所有合法组合都会被访问到。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> ans;
string cur;
function<void(int, int)> dfs = [&](int open, int close) {
if (open == n && close == n) {
ans.push_back(cur);
return;
}
if (open < n) {
cur.push_back('(');
dfs(open + 1, close);
cur.pop_back();
}
if (close < open) {
cur.push_back(')');
dfs(open, close + 1);
cur.pop_back();
}
};
dfs(0, 0);
return ans;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
for (auto &s : Solution().generateParenthesis(n))
cout << s << '\n';
return 0;
}python
#!/usr/bin/env python3
from typing import List
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
ans, cur = [], []
def dfs(open, close):
if open == n and close == n:
ans.append("".join(cur))
return
if open < n:
cur.append("(")
dfs(open + 1, close)
cur.pop()
if close < open:
cur.append(")")
dfs(open, close + 1)
cur.pop()
dfs(0, 0)
return ans
def main():
n = int(input())
for s in Solution().generateParenthesis(n):
print(s)
if __name__ == "__main__":
main()复杂度
- 时间复杂度:
,其中 是 Catalan 数,即合法组合的数量。每个组合需要 步构建。 - 空间复杂度:
,递归栈深度为 。
总结
括号生成是 Catalan 数的经典应用。关键剪枝条件 close < open 保证任意前缀中右括号不超过左括号,等价于只保留搜索树中合法的分支。这与"从左到右扫描括号序列"的合法性判定完全一致。