leetcode22-括号生成

208 阅读2分钟

题目

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入: n = 3

输出: ["((()))","(()())","(())()","()(())","()()()"]

来源:力扣(LeetCode)

链接:leetcode.cn/problems/ge…

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

代码

暴力递归

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了,当走到最后一个节点得到答案后,删掉 当前节点,往前回溯,走另外一种可能

总结

典型的回溯题目,就是基于暴力递归的优化