22. 括号生成
题目:
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:
输入:n = 3
输出:[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
2. 题解
2.1 解法1 : dfs
递归状态记录: 当前括号path, 左括号数量, 右括号数量, 以及 n
class Solution {
public List<String> generateParenthesis(int n) {
List<String> ans = new ArrayList<>();
int lc = 0, rc = 0;
dfs(ans, "", n, lc, rc);
return ans;
}
public void dfs(List<String> ans, String path, int n, int lc, int rc) {
if (rc > lc || lc > n || rc > n) return;
if (lc == rc && lc == n) {
ans.add(path);
return;
}
dfs(ans, path + '(', n, lc + 1, rc);
dfs(ans, path + ')', n, lc, rc + 1);
}
}
2.2 题解2:暴力递归法(通过递归生成所有括号组合,然后判断其是否有效)
主要流程:
- 编写判断括号组合是否有效的函数,主要方法为
- 遍历该组合序列,使用balance记录左括号减去右括号的数量,
- 若遍历过程中balance<0或遍历结束时balance不为0,则序列无效
- 编写递归函数。主要流程如下:
- 先判断深度是否达到n,若达到,则判断该组合序列是否有效,有效加入结果集,返回
- 否则深度为达到n,继续向下生成,添加左右括号
代码:
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
combination(res, n, 0, "");
return res;
}
//递归函数:参数分别为,结果集,最大深度,深度,当前结点字符串
public void combination(List<String> res, int n, int index, String s) {
if (index == 2 * n) {//注意这里为2n,因为是括号组合,所以应该是两倍长度
if (isValid(s)) {
res.add(s);
}
return;
} else {
combination(res, n, index + 1, s + "(");
combination(res, n, index + 1, s + ")");
}
}
//有效判断函数
public boolean isValid(String s) {
int balance = 0;
for (int i = 0; i < s.length(); i++) {
if (balance < 0) {
return false;
}
if (s.charAt(i) == '(') {
balance++;
} else if (s.charAt(i) == ')') {
balance--;
}
}
return balance == 0;
}
}
2.3 题解3:回溯法
主要思路:
- 基于解法一的改进,加入剪枝,递归调用前判断,当左括号数量小于n,右括号数量小于左括号数量时才执行递归
代码:
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
combination(res, n, 0, 0, "");
return res;
}
//递归函数,参数分别为,结果集,括号数量,当前结点左括号数量,右括号数量,字符串
public void combination(List<String> res, int n, int left, int right, String s) {
if (s.length() == 2 * n) {//注意这里为2n,因为是括号组合,所以应该是两倍长度
if (isValid(s)) {
res.add(s);
}
return;
} else {
if (left < n) {
combination(res, n, left + 1, right, s + "(");
}
if (left > right) {
combination(res, n, left, right + 1, s + ")");
}
}
}
//有效判断函数
public boolean isValid(String s) {
int balance = 0;
for (int i = 0; i < s.length(); i++) {
if (balance < 0) {
return false;
}
if (s.charAt(i) == '(') {
balance++;
} else if (s.charAt(i) == ')') {
balance--;
}
}
return balance == 0;
}
}
2.4 题解4:动态规划
主要思路:
- 设置状态,
dp[i]
,表示括号对数为i时的所有有效括号列表(结果集) - 现在由
dp[i-1]
递推dp[i]
的结果 - 下一个结果,开头的元素必定是左括号,然后需要找到与之匹配的右括号,而其所匹配的右括号存在的位置可能在末尾也可能在中间,所以可以得到递推公式如下:
dp[i]="("+dp[可能的括号组合]+")"+dp[剩下的括号组合]
,且可能的括号组合+剩下的括号组合=i-1,
- 设 可能括号组合为
j
,剩下括号组合为i-j-1 (j=0,1...,i-1)
那么递推公式可写为:
dp[i]="("+dp[j]+")"+dp[i-j-1]; (j=0,1...,i-1)
代码:
class Solution {
public List<String> generateParenthesis(int n) {
List<List<String>> dp = new ArrayList<>();
//初始化dp0
List<String> dp0 = new ArrayList<>();
dp0.add("");
dp.add(dp0);
//循环递推
for (int i = 1; i <= n; i++) {
//下面推出dp[i]
List<String> temp = new ArrayList<>();//存储dp[i]下的所有组合
for (int j = 0; j < i; j++) {//设置可能括号组合
//将dp[j]中所有的括号组合拿出来推
for (String a : dp.get(j)) {
for (String b : dp.get(i - j - 1)) {
temp.add("(" + a + ")" + b);//得到一种组合
}
}
}
dp.add(temp);//最终将得到的列表赋值给dp[i]
}
return dp.get(n);
}
}