题目描述
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1
输出:["()"]
题解
题解转载自liweiwei1419
深度优先遍历法
解法除了最后一步之外每一步都和前一步处理方法相同:从剩余的左右括号中选择一使得匹配成立,所以使用递归
我们以 n = 2 为例,画树形结构图。方法是 「做减法」。
画图以后,可以分析出的结论:
当前左右括号都有大于 0 个可以使用的时候,才产生分支;
产生左分支的时候,只看当前是否还有左括号可以使用;
产生右分支的时候,还受到左分支的限制,右边剩余可以使用的括号数量一定得在严格大于左边剩余的数量的时候,才可以产生分支;
在左边和右边剩余的括号数都等于 0 的时候结算。
代码实现
1. String+不回溯
class Solution {
List<String> res = new ArrayList<>();
public List<String> generateParenthesis(int n) {
if(n==0) return res;
dfs(n,n,"");
return res;
}
//left:剩余左括号的数目 right: 剩余右括号的数目 s:当前拼接的字符串
public void dfs(int left,int right,String curStr){
//递归终止条件:所有左右括号均已被使用
if(left==0&&right==0){
//为什么不是new String(curStr)
res.add(curStr);
return;
}
// 因为左括号一定出现在右括号前,所以当right<left直接剪枝
if(right<left){
return;
}
//括号树生成左枝叶的条件:左括号数>0
if(left>0){
dfs(left-1,right,curStr+"(");
}
if(right>left){
dfs(left,right-1,curStr+")");
}
}
}
为什么此题不需要new String(curStr),而且不需要回溯(撤销上一步的动作)?
主要是跟字符串的特点有关哈,Java 里 + 生成了新的字符串,每次往下面传递的时候,都是新字符串。因此在搜索的时候不用回溯。
可以想象搜索遍历的问题其实就像是做实验,每一次实验都用新的实验材料,那么做完了就废弃了。但是如果只使用一份材料,在做完一次以后,一定需要将它恢复成原样(就是这里「回溯」的意思),才可以做下一次尝试。
2.StringBuilder+回溯
如果不用String而是使用 StringBuilder 全程只使用一份变量去搜索的做法,则需要回溯
import java.util.ArrayList;
import java.util.List;
public class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
if (n == 0) {
return res;
}
StringBuilder path = new StringBuilder();
dfs(path, n, n, res);
return res;
}
/**
* @param path 从根结点到任意结点的路径,全程只使用一份
* @param left 左括号还有几个可以使用
* @param right 右括号还有几个可以使用
* @param res
*/
private void dfs(StringBuilder path, int left, int right, List<String> res) {
if (left == 0 && right == 0) {
// path.toString() 生成了一个新的字符串,相当于做了一次拷贝,这里的做法等同于「力扣」第 46 题、第 39 题
res.add(path.toString());
return;
}
// 剪枝(如图,左括号可以使用的个数严格大于右括号可以使用的个数,才剪枝,注意这个细节)
if (left > right) {
return;
}
if (left > 0) {
path.append("(");
dfs(path, left - 1, right, res);
path.deleteCharAt(path.length() - 1);
}
if (right > 0) {
path.append(")");
dfs(path, left, right - 1, res);
path.deleteCharAt(path.length() - 1);
}
}
}