LeetCode —— 22「括号生成」

105 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情

一、题目

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

二、示例

示例 1:

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

示例 2:

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

三、题解

回溯法

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> ans = new ArrayList<String>();
        backtrack(ans, new StringBuilder(), 0, 0, n);
        return ans;
    }

    public void backtrack(List<String> ans, StringBuilder cur, int open, int close, int max) {
        if (cur.length() == max * 2) {
            ans.add(cur.toString());
            return;
        }
        if (open < max) {
            cur.append('(');
            backtrack(ans, cur, open + 1, close, max);
            cur.deleteCharAt(cur.length() - 1);
        }
        if (close < open) {
            cur.append(')');
            backtrack(ans, cur, open, close + 1, max);
            cur.deleteCharAt(cur.length() - 1);
        }
    }
}

复杂度分析

  • 时间复杂度 O(N)
  • 空间复杂度 O(N)

解题思路

暴力解

可以先递归出所有的可能性,再依次判断其有效性

优化1:

基于此,我们可以在暴力解的过程中,通过回溯提前判断其有效性,例如左括号 < n的时候才可以再放左括号,右括号数量小于左括号时才能放右括号,否则必定无效

我们可以只在序列仍然保持有效时才添加 \text{('}‘(’ 或 \text{)'}‘)’。我们可以通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点,

同时这也校验了其有效性

优化2:

任何一个括号序列都一定是由 \text{('}‘(’ 开头,并且第一个 \text{('}‘(’ 一定有一个唯一与之对应的 \text{)'}‘)’。这样一来,每一个括号序列可以用 (a)b(a)b 来表示,其中 aa 与 bb 分别是一个合法的括号序列(可以为空)。

那么,要生成所有长度为 2n2n 的括号序列,我们定义一个函数 \textit{generate}(n)generate(n) 来返回所有可能的括号序列。那么在函数 \textit{generate}(n)generate(n) 的过程中:

我们需要枚举与第一个 \text{('}‘(’ 对应的 \text{)'}‘)’ 的位置 2i + 12i+1; 递归调用 \textit{generate}(i)generate(i) 即可计算 aa 的所有可能性; 递归调用 \textit{generate}(n - i - 1)generate(n−i−1) 即可计算 bb 的所有可能性; 遍历 aa 与 bb 的所有可能性并拼接,即可得到所有长度为 2n2n 的括号序列。 为了节省计算时间,我们在每次 \textit{generate}(i)generate(i) 函数返回之前,把返回值存储起来,下次再调用 \textit{generate}(i)generate(i) 时可以直接返回,不需要再递归计算。 `

算法

原题:LeetCode