059括号生成

括号生成

题目链接:https://leetcode.cn/problems/generate-parentheses/description/?envType=study-plan-v2\&envId=top-100-liked

我的解答:

复制代码
public List<String> generateParenthesis(int n) {
    List<String> ans = new ArrayList<>();
    backtreack(n, 0, 0, new StringBuffer(), ans);
    return ans;
}
//left:左括号的个数    right:右括号的个数
public void backtreack(int n, int left, int right, StringBuffer output, List<String> ans){
    if(right == n){
        ans.add(output.toString());
        return;
    }
    if(left + 1 <= n){
        //选取左括号
        left++;
        output.append('(');
        backtreack(n, left, right, output, ans);
        //回溯
        left--;
        output.deleteCharAt(output.length()-1);
    }

    if(right + 1 <= left){
        //选取右括号
        right++;
        output.append(')');
        backtreack(n, left, right, output, ans);
        //回溯
        right--;
        output.deleteCharAt(output.length()-1);
    }
}

分析:代码的时间复杂度计算复杂,详情请参考官方解析方法二中的时间复杂度分析,空间复杂度为O(n)。解题思路:采用递归 + 回溯方法,思路简单,故不赘述。

看了官方题解后的解答:

复制代码
//方法一:暴力法(思路简单,效率低,故直接粘贴了官方的解答)
//时间复杂度:O(n*2^2n),对于2^2n个序列中的每一个,我们用于建立和验证该序列的复杂度为 O(n)。
//空间复杂度:O(n)
public List<String> generateParenthesis(int n) {
    List<String> combinations = new ArrayList<String>();
    generateAll(new char[2 * n], 0, combinations);
    return combinations;
}

public void generateAll(char[] current, int pos, List<String> result) {
    if (pos == current.length) {
        if (valid(current)) {
            result.add(new String(current));
        }
    } else {
        current[pos] = '(';
        generateAll(current, pos + 1, result);
        current[pos] = ')';
        generateAll(current, pos + 1, result);
    }
}

public boolean valid(char[] current) {
    int balance = 0;
    for (char c: current) {
        if (c == '(') {
            ++balance;
        } else {
            --balance;
        }
        if (balance < 0) {
            return false;
        }
    }
    return balance == 0;
}

//方法二:回溯法(此方法与我的解答一致,但我参考官方解答将我的解答略微优化了一些多于的步骤)
//时间复杂度:本方法的时间复杂度计算复杂,详情请参考官方解析
//空间复杂度:O(n)
public List<String> generateParenthesis(int n) {
    List<String> ans = new ArrayList<>();
    backtreack(n, 0, 0, new StringBuffer(), ans);
    return ans;
}
//left:左括号的个数    right:右括号的个数
public void backtreack(int n, int left, int right, StringBuffer output, List<String> ans){
    if(right == n){
        ans.add(output.toString());
        return;
    }
    if(left < n){
        //选取左括号
        output.append('(');
        backtreack(n, left+1, right, output, ans);
        //回溯
        output.deleteCharAt(output.length() - 1);
    }

    if(right < left){
        //选取右括号
        output.append(')');
        backtreack(n, left, right+1, output, ans);
        //回溯
        output.deleteCharAt(output.length() - 1);
    }
}

//方法三:按括号序列的长度递归
//本方法的时间复杂度和空间复杂度计算复杂,详情请参考官方解析
ArrayList[] cache = new ArrayList[9];
public List<String> generateParenthesis(int n) {
    return generate(n);
}
public List<String> generate(int n){
    if(cache[n] != null){
        return cache[n];
    }
    ArrayList<String> res = new ArrayList<>();
    if(n == 0){
        res.add("");
    }
    else{
        for(int i=0; i<n; i++){
            for(String left : generate(i)){
                for(String right : generate(n-1-i)){
                    res.add("(" + left + ")" + right);
                }
            }
        }
    }
    cache[n] = res;
    return res;
}

分析:

​ 1、方法一采用暴力枚举,每次对枚举出的结果进行验证,若是有效答案,则加入结果。

​ 2、方法二在方法一的基础上进行了优化,递归前先通过左括号和右括号的个数进行判断,保证递归的答案一定是有效的。

​ 3、方法三的解题思路:对于每一个有效的括号序列,一定是以"("开头,以")"结尾的,所以每一个有效的括号序列都是 (a)b 的形式,其中a和b既可以为空,也可以是有效的括号序列。经过以上分析,我们只需递归计算 a 的所有可能和 b 的所有可能,遍历 a 与 b 的所有可能性并拼接,即可得到所有长度为 2n 的括号序列。另外,为了节省计算时间,我们可以在每次 generate(i) 函数返回之前,把返回值存储起来,下次再调用 generate(i) 时可以直接返回,不需要再递归计算。

总结

  • 本题只需掌握基本的递归与回溯即可。
  • 对于本题的方法三,我们需要善于观察与总结,关键在于"对于每一个有效的括号序列,一定是以"("开头,以")"结尾的,所以每一个有效的括号序列都是 (a)b 的形式,其中a和b既可以为空,也可以是有效的括号序列"这个结论,在这个结论的基础上可以很轻松的得出"a、b可能性拼接"的解题方法。
相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark3 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her13 天前
并查集-听课笔记
笔记·算法·并查集
码流子3 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven3 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark3 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218613 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法