数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:
输入:n = 3
输出:[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]
方法一:暴力枚举
枚举出括号的所有排列。为了生成所有的序列,可以使用递归的方法。长度为n的序列就是在长度为n-1的序列后面加一个“(”或“)”。
class Solution {
// 用来检查一个排列是否符合要求
bool valid (const string & str) {
int balance = 0;
for (char c : str) {
if (c == '(') {
++balance;
}
else {
--balance;
if (balance < 0) {
return false;
}
}
}
return balance == 0;
}
// 用来生成所有排列,使用回溯法
void generateAll (string& current, int n, vector<string>& result) {
if (n == current.size()) {
if (valid(current)) {
result.push_back(current);
}
return;
}
current += '(';
generateAll(current, n, result);
current.pop_back();
current += ')';
generateAll(current, n, result);
current.pop_back();
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
generateAll(current, n*2, result);
return result;
}
};
方法二:有限制条件的回溯
对于方法一:我们可以只在序列仍然保持有效时才添加“(”和“)”,而不是像方法一一样每次都添加,可以通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点。如果左括号数量不大于n,可以放一个左括号。如果右括号数量小于左括号的数量,可以放一个右括号。
class Solution {
void backtrack(vector<string>& ans, string& cur, int open, int close, int n) {
// open标记左括号的个数,close标记右括号的个数
if (cur.size() == n*2) { // cur已经是一个符合要求的排列
ans.push_back(cur);
return;
}
if (open < n) { // open标记左括号的个数,如果左括号的个数小于n,可以添加一个左括号
cur.push_back('(');
backtrack(ans, cur, open+1, close, n);
cur.pop_back();
}
if (close < open) { // 如果右括号的个数小于左括号的个数,则可以添加右括号
cur.push_back(')');
backtrack(ans, cur, open, close+1, n);
cur.pop_back();
}
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
backtrack(result, current, 0, 0, n);
return result;
}
};