[DFS|剪枝] leetcode 22 括号生成
1.题目
题目链接
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。
示例:
输入:n = 3
输出:[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
2.分析
2.1.直观解法
为了求某种有效的组合,我们可以把所有可能的组合都列出来,然后逐个判断各个组合是否有效。这就是所谓的暴力法。
在这里,首先是要判断一个字符串是否为合法的括号。直接顺序扫描整个字符串,令balance=左括号个数-右括号个数。如果balance < 0,则表示左括号比右括号少,为非法括号串;若遍历完后balance不为零,表示左右括号数不相等,为非法括号串。
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;
}
再给出dfs的写法。依旧是遵从一般dfs模板的原则:
- 参数中记录当前遍历进度
- 进入方法后首先判断是否已生成一个新的组合
- 若是,则判断该组合是否有效;若否,则继续生成每种组合
在这里,当前进度为我们已构造括号串的长度,当长度为2n时表示我们生成了一种可能的括号串,需要进一步判断;否则,为当前串加上(或),继续括号串的构建。
void dfs(string str, int n) {
int k = str.length();
if(k == 2 * n) {
if(valid(str)){
res.push_back(str);
}
return;
}
dfs(str + '(', n);
dfs(str + ')', n);
}
vector<string> generateParenthesis(int n) {
dfs("", n);
return res;
}
2.2.剪枝
考虑对以上的方法进行改进,也就是所谓的"剪枝",提前排除一些非法的情况。
对于上面的暴力法,如果我们当前构造出来的括号串为)),实际上其已经是一个非法的括号串,因此也就没有必要继续进行搜索了。因此我们将判断合法性的过程融入在构造新串的过程中,提前删除无效情况。
- 添加左括号:左括号最多有n个。如果左括号数已经大于n - 1,便不允许继续添加左括号。
- 添加右括号:右括号个数的判断逻辑同左括号。此外,如果右括号数已经大于等于左括号数,此时也不允许继续添加右括号。
void dfs(string str, int n, int l, int r) {
int k = str.length();
if(k == 2 * n) {
res.push_back(str);
return;
}
if(l < n) {
dfs(str + '(', n, l + 1, r);
}
if(r < n && l > r) {
dfs(str + ')', n, l, r + 1);
}
}
3.代码
class Solution {
public:
vector<string> res;
void dfs(string str, int n, int l, int r) {
int k = str.length();
if(k == 2 * n) {
res.push_back(str);
return;
}
if(l < n) {
dfs(str + '(', n, l + 1, r);
}
if(r < n && l > r) {
dfs(str + ')', n, l, r + 1);
}
}
vector<string> generateParenthesis(int n) {
dfs("", n, 0, 0);
return res;
}
};