题目
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入: n = 3
输出: ["((()))","(()())","(())()","()(())","()()()"]
来源:力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
暴力递归
public class Leetcode22 {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
allParenthesis(new char[2*n],res,0,0,0);
return res;
}
private void allParenthesis(char[] chars, List<String> res, int index, int leftNum, int rightNum) {
int length = chars.length;
if(leftNum+rightNum == length){
if(isValid(chars)){
res.add(new String(chars));
}
}
if(leftNum<length/2){
chars[index] = '(';
allParenthesis(chars,res,index+1,leftNum+1,rightNum);
}
if(rightNum<length/2){
chars[index] = ')';
allParenthesis(chars,res,index+1,leftNum,rightNum+1);
}
}
private boolean isValid(char[] chars) {
int valid = 0;
for (int i = 0; i < chars.length; i++) {
if(chars[i]=='('){
valid++;
}else {
valid--;
}
if(valid<0){
return false;
}
}
return true;
}
}
法二:回溯
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
// allParenthesis(new char[2 * n], res, 0, 0, 0);
back(n*2,res,new StringBuilder(),0,0);
return res;
}
private void back(int length, List<String> res, StringBuilder str, int leftNum, int rightNum) {
if (str.length() == length) {
res.add(str.toString());
return;
}
// 只有在左括号大于右括号的情况下 才能添加右括号
if(leftNum>rightNum){
back(length,res,str.append(')'),leftNum,rightNum+1);
// 完成上面的递归后,回溯删除一个节点,给其他选择机会;比如刚刚添加了一个(,组成了一个组合,然后删掉这个节点,走下面的添加(逻辑
str.deleteCharAt(str.length()-1);
}
// 如果没超过一半
if(leftNum<length/2){
back(length,res,str.append('('),leftNum+1,rightNum);
// 逻辑同上,即便这里是(,因为可能是中间过程的回溯
str.deleteCharAt(str.length()-1);
}
}
}
解析
法一:暴力,题目没有时间限制,直接暴力,将所有的可能通过递归列出来,然后在判断符不符合即可;添加期间,判断下是否有没有超过一半即可
法一:回溯,主要是对上面的暴力优化,1. 可以看到,对于每个index,都有两种可能;( 或者 ),然后根据()的匹配原则,遍历过去时,(的数量一定要大于等于),所以可以筛掉一部分选择;2.既然是在符合条件做的选择,也就不用判断valid了,当走到最后一个节点得到答案后,删掉 当前节点,往前回溯,走另外一种可能
总结
典型的回溯题目,就是基于暴力递归的优化