LeetCode 👉 HOT 100 👉 括号生成 - 中等题

535 阅读1分钟

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

题目

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

有效括号组合需满足:左括号必须以正确的顺序闭合。

示例1

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]

示例2

输入:n = 1
输出:["()"]

思路

最初的想法是把一个括号 () 当成一个整体,每次操作的都是一个括号,具体想法如下:

  • 初始时,已有括号组合为 ''

  • 每次使用一个括号,当前这个括号可能出现的位置,是上一步括号组合的所有缝隙中,例如 (),有 1(2)3 三个位置可能

  • Set 结构存储已有组合,用于去重

  • 将当前这个括号插入缝隙中,判断当前组合是否存在,不存在则放入结果数组中

  • n 个括号使用完,即可得出答案

图解

借用leetCode大佬的图

代码如下

    /**
     * @param {number} n 1 <= n <= 8
     * @return {string[]}
     */
    var generateParenthesis = function(n) {
        
        let ans = ['()'], parenthesisSet = new Set();


        // 获取 str 加一个 () 所有可能的组合
        const getNext = str => {
            let i = 0, nextAns = [];

            // 遍历 () 可能插入的位置
            while(i < str.length) {
                let newStr = str.slice(0,i) + '()' + str.slice(i);

                if(!parenthesisSet.has(newStr)) {
                    nextAns.push(newStr);
                    parenthesisSet.add(newStr)
                }
                i++;
            }

            return nextAns;
        }

        for(let i = 1; i < n; i++) {
            // 使用 第 i + 1 个括号得出的结果
            let tempAns = [];

            // 遍历上一步所有的组合
            for(let j = 0; j < ans.length; j++) {
                // 每个组合加上一个括号,可能的组合
                tempAns = tempAns.concat(getNext(ans[j]))
            }

            // 将当前的结果重新赋值给 ans
            ans = tempAns
        }

        return ans;
    };

思考: 上述的解法,将括号视为一个整体,避免了无效组合的情况,例如 ())(,那么如果将括号拆分开,该怎么做呢?

可以想到,n 个括号,会分别有 n 个左括号和 n 个右括号,从 '' 开始,

  • 假如使用一个 (,当前字符变成了 (,此时还剩 n - 1 个左括号和 n 个右括号;

    • 假如再次使用一个 (,当前字符变成了 ((,此时还剩 n - 2 个左括号和 n 个右括号;

    • 假如再次使用一个 ),当前字符变成了 (),此时还剩 n - 1 个左括号和 n - 1 个右括号;

  • 假如使用一个 ),当前字符变成了 ),此时还剩 n 个左括号和 n - 1 个右括号;

    • 这种情况不合理,因为以 ) 开头的组合,必然是无效组合

我们重复上述步骤,把所有的左右括号都使用完,就会得到一种合理的组合

代码如下

    /**
     * @param {number} n 1 <= n <= 8
     * @return {string[]}
     */
    var generateParenthesis = function(n) {

        let ans = [];
        
        const dfs = (str, left, right) => {

            if(right > left || left > n) return;
            if(left == n && right == n) {
                ans.push(str);
            }

            dfs(`${str}(`, left + 1, right);
            dfs(`${str})`, left, right + 1);
        }

        dfs('', 0, 0)

        return ans;
    };

小结

上面的第二种解法,可以称之为回溯,回溯算法的实质就是逐步枚举所有的可能性,在枚举的过程中,去掉无效的分枝(剪枝),提前结束递归或者枚举。

LeetCode 👉 HOT 100 👉 括号生成 - 中等题

合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍

如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄