在问 · 算法题解

22. 括号生成

中等回溯计数剪枝回溯力扣原题 ↗

题目描述

输入整数 n,表示有 n 个 '(' 和 n 个 ')'。

用它们拼成长度为 2n 的字符串,要求括号能完全配对:从左往右扫描时,任何时刻 ')' 的个数都不超过 '(' 的个数。

返回所有符合要求的字符串,顺序不限。

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

解释:3 对括号共有 5 种合法组合。

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

解释:只有一对括号,唯一的组合是 "()"。

数据范围

  • 1 <= n <= 8

解题思路:计数剪枝回溯

限制左、右括号数量后回溯。

为什么正确

任意前缀都满足右括号数不大于左括号数。

复杂度分析

时间 O(4ⁿ/√n),额外空间 O(n)。

易错点

注意边界条件并在回溯或弹出后恢复状态。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
 def generateParenthesis(self,n):
  out=[];p=[]
  def f(l,r):
   if l==r==n:out.append(''.join(p));return
   if l<n:p.append('(');f(l+1,r);p.pop()
   if r<l:p.append(')');f(l,r+1);p.pop()
  f(0,0);return out

载入编辑器,自己跑一遍 →

其他解法

暴力枚举先生成再校验,代价是 2²ⁿ;回溯边生成边剪枝,只访问合法前缀;分解法按 (a)b 结构组合子问题,复杂度与回溯同级但要存下中间结果。

解法 2:暴力枚举 + 校验 时间 O(2²ⁿ·n),空间 O(n)

递归生成所有长度为 2n 的括号串,每一位都尝试放 ( 和 )。串长达到 2n 时,从左到右维护余额:遇到 ( 加 1,遇到 ) 减 1。余额从未小于 0 且最终为 0 的串才加入答案。

不需要任何剪枝思路,正确性一目了然。它会生成 2²ⁿ 个串,绝大多数无效;主解法把校验条件提前到放括号的那一刻,只走合法分支。

class Solution:
    def generateParenthesis(self, n):
        out = []
        p = []

        def valid():
            bal = 0
            for ch in p:
                bal += 1 if ch == '(' else -1
                if bal < 0:
                    return False
            return bal == 0

        def f():
            if len(p) == 2 * n:
                if valid():
                    out.append(''.join(p))
                return
            p.append('('); f(); p.pop()
            p.append(')'); f(); p.pop()

        f()
        return out

解法 3:按首对括号分解 时间 O(4ⁿ/√n),空间 O(4ⁿ/√n)

任何非空合法串都能唯一写成 (a)b,其中 a 含 c 对括号,b 含 n-1-c 对括号,a、b 都合法。gen(k) 枚举 c 从 0 到 k-1,把 gen(c) 与 gen(k-1-c) 的结果两两组合。用哈希表记住每个 k 的结果,gen(0) 只含空串。

直接对应卡特兰数的递推式,适合理解答案个数为什么是卡特兰数。需要保存所有较小规模的结果,内存比回溯大。

class Solution:
    def generateParenthesis(self, n):
        memo = {0: ['']}

        def gen(k):
            if k in memo:
                return memo[k]
            res = []
            for c in range(k):
                for left in gen(c):
                    for right in gen(k - 1 - c):
                        res.append('(' + left + ')' + right)
            memo[k] = res
            return res

        return gen(n)

回溯 专题的其他题目

查看 回溯 全部题目 →