一、题目
二、题解
dfs
题目给出n,那么结果中一定会有n对括号,也就是2 * n个字符,我们只需要枚举这2n个字符的每一位即可,每一位有两种选择:选择 ( 或者 选择 ),我们只需要在递归的出口收集结果即可,决策树如下
对括号序列进行合法性判断:
class Solution {
public:
vector<string> res;
string path;
int n;
bool islegal(string path) {
if (path.size() <= 0) return true;
char stack[n * 2];
int top = -1;
for (auto x : path) {
if (x == '(') stack[++top] = x;
else {
if (top < 0) return false;
else if (x == ')' && stack[top] == '(') top--;
else return false;
}
}
return top == -1;
}
int cnt;
void dfs(int u) {
cnt ++;
if (u == 2 * n) {
if (islegal(path))
res.push_back(path);
return;
}
path += '(';
dfs(u + 1);
path.pop_back();
path += ')';
dfs(u + 1);
path.pop_back();
}
vector<string> generateParenthesis(int _n) {
n = _n;
return res;
}
};
小优化
上述dfs过程中,我们发现有在dfs过程中有一些分支是没有必要继续去dfs的,比如 在第一个位置选择时,我们选择右括号,右括号开头他注定不是一个合法的括号序列,所以这个分支我们没必要继续枚举后面不是答案的情况,再比如 ())这种也是无效的括号序列,不难发现在dfs过程中无效的括号序列在除去前面有效部分之后,是以)开头的 ())取出有效括号()后剩余) 剪枝前后dfs次数对比
class Solution {
public:
vector<string> res;
string path;
int n;
// 判断是否需要进行剪枝
bool isForget(string path) {
if (path.size() <= 0) return true;
char stack[n * 2];
int top = -1;
for (auto x : path) {
if (x == '(') stack[++top] = x;
else {
if (top < 0) return false;
else if (x == ')' && stack[top] == '(') top--;
else return false;
}
}
return top == -1 || stack[top] == '(' ;
}
// 判断序列是否合法
bool islegal(string path) {
if (path.size() <= 0) return true;
char stack[n * 2];
int top = -1;
for (auto x : path) {
if (x == '(') stack[++top] = x;
else {
if (top < 0) return false;
else if (x == ')' && stack[top] == '(') top--;
else return false;
}
}
return top == -1;
}
void dfs(int u) {
if (u == 2 * n) { // 递归出口收集答案
if (islegal(path))
res.push_back(path);
return;
}
if (!isForget(path)) return; // 剪枝
path += '('; // 选择 (
dfs(u + 1);
path.pop_back();
path += ')'; // 选择 )
dfs(u + 1);
path.pop_back();
}
vector<string> generateParenthesis(int _n) {
n = _n;
dfs(0);
return res;
}
};