当前左右括号都有大于 00 个可以使用的时候,才产生分支;
产生左分支的时候,只看当前是否还有左括号可以使用;
产生右分支的时候,还受到左分支的限制,右边剩余可以使用的括号数量一定得在严格大于左边剩余的数量的时候,才可以产生分支;
在左边和右边剩余的括号数都等于 00 的时候结算。
curStr 当前递归得到的结果
* @param left 左括号还有几个可以使用
* @param right 右括号还有几个可以使用
* @param res 结果集
树状分枝,假设走到左或者右
class Solution {
public List<String> generateParenthesis(int n) {
//做减法,减枝return
List<String> res=new ArrayList<>();
if(n==0) return null;
dfs("",n,n,res);
return res;
}
private void dfs(String cur,int left,int right,List<String>res){
if(left==0&&right==0){
res.add(cur);
return;
}
//使用有括号的那条路,然后看left和right
if(left >right){
return ;
}
if(left>0){
dfs(cur+"(",left-1,right,res);
}
if(right>0){
dfs(cur+")",left,right-1,res);
}
}
}
主要是跟字符串的特点有关哈,Java 和 Python 里 +
生成了新的字符串,每次往下面传递的时候,都是新字符串。因此在搜索的时候不用回溯。
可以想象搜索遍历的问题其实就像是做实验,每一次实验都用新的实验材料,那么做完了就废弃了。但是如果只使用一份材料,在做完一次以后,一定需要将它恢复成原样(就是这里「回溯」的意思),才可以做下一次尝试。
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);
}
}