【回溯-2】22.括号生成

题目描述:

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

示例 1:

复制代码
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]

示例 2:

复制代码
输入:n = 1
输出:["()"]

解题思路:

方法一:回溯 + 剪枝

有效括号的条件:

  1. 左括号数量 ≤ n

  2. 右括号数量 ≤ 左括号数量 (否则会出现 )( 这种无效组合)

  3. 左括号数量 == 右括号数量 == n 时,得到一个有效组合

回溯三步:

  1. 选择:放左括号或右括号

  2. 递归:继续生成下一个括号

  3. 撤销:删除当前括号

具体过程示例:

n = 2

复制代码
              ""
            /    \
          (       (不合法,右括号多于左括号)
        /   \
      ((     ()
     /  \      \
   (()  ()(    ())  ← ()) 不合法,右括号多于左括号
   /      \
 (())    ()()

有效组合 :(())、()()

代码实现:

cpp 复制代码
class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> result;
        string path;
        backtrack(n, 0, 0, path, result);
        return result;
    }
    
private:
    void backtrack(int n, int left, int right, string& path, vector<string>& result) {
        // 终止条件:左右括号都用完了
        if (left == n && right == n) {
            result.push_back(path);
            return;
        }
        
        // 选择左括号:只要左括号没用完
        if (left < n) {
            path.push_back('(');
            backtrack(n, left + 1, right, path, result);
            path.pop_back();  // 撤销
        }
        
        // 选择右括号:只要右括号数量 < 左括号数量
        if (right < left) {
            path.push_back(')');
            backtrack(n, left, right + 1, path, result);
            path.pop_back();  // 撤销
        }
    }
};

复杂度分析:

维度 复杂度 说明
时间复杂度 O(4^n / √n) 第 n 个卡特兰数,约等于 4^n / (n√n)
空间复杂度 O(n) 递归栈深度 + path 长度

卡特兰数 :C(2n, n) / (n + 1),是有效括号组合的数量。

关键细节:

1. 为什么 right < left 才能放右括号?

因为有效括号要求任意前缀中左括号数量 ≥ 右括号数量。

如果 right >= left,放右括号会导致 ) 多于 (,形成无效组合。

2. 为什么 left < n 才能放左括号?

因为总共只有 n 对括号,左括号最多放 n 个。

3. 为什么不用判断 right < n?

因为 right < left 已经隐含了 right < n(因为 left ≤ n)。

4. 终止条件为什么是 left == n && right == n?

当左右括号都用完时,path 就是一个完整的有效组合。

方法二:暴力枚举

生成所有 2^(2n) 个括号组合,然后验证每个是否有效。

代码实现:

cpp 复制代码
class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> result;
        generateAll(n, 0, "", result);
        return result;
    }
    
private:
    void generateAll(int n, int pos, string current, vector<string>& result) {
        if (pos == 2 * n) {
            if (isValid(current)) result.push_back(current);
            return;
        }
        generateAll(n, pos + 1, current + "(", result);
        generateAll(n, pos + 1, current + ")", result);
    }
    
    bool isValid(string& s) {
        int balance = 0;
        for (char c : s) {
            if (c == '(') balance++;
            else balance--;
            if (balance < 0) return false;
        }
        return balance == 0;
    }
};

复杂度:时间 O(2^(2n) × n),空间 O(n)

缺点:枚举了大量无效组合,效率低。

方法三:动态规划

思路 :n 对括号的组合 = ( + i 对括号 + ) + n-1-i 对括号

代码实现:

cpp 复制代码
class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<vector<string>> dp(n + 1);
        dp[0] = {""};
        
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < i; j++) {
                for (string& left : dp[j]) {
                    for (string& right : dp[i - 1 - j]) {
                        dp[i].push_back("(" + left + ")" + right);
                    }
                }
            }
        }
        
        return dp[n];
    }
};

复杂度:时间 O(4^n / √n),空间 O(4^n / √n)

三种方法对比:

方法 时间复杂度 空间复杂度 推荐度
回溯 + 剪枝 O(4^n / √n) O(n) ⭐⭐⭐⭐⭐
暴力枚举 O(2^(2n) × n) O(n) ⭐⭐
动态规划 O(4^n / √n) O(4^n / √n) ⭐⭐⭐

总结:

要点 说明
核心思想 回溯:放左括号或右括号,剪枝保证有效
关键条件 left < n 放左括号,right < left 放右括号
时间复杂度 O(4^n / √n)
空间复杂度 O(n)
相关推荐
凤凰院凶涛QAQ19 小时前
《Java版数据结构 & 集合类剖析》Map & Set & 反射:“从哈希碰撞到红黑树转换,反射帮你扒开 HashMap 的桶数组看个究竟”
java·数据结构·算法
H.莓飛19 小时前
【数据结构】排序_OJ题(一):计数、桶与排序后的处理
java·开发语言·数据结构
w_zero_one21 小时前
算法--模拟
java·数据结构·算法
秋天的怀念_David21 小时前
算法一千零一夜 · 第十三夜 幻方,武侠世界里的数学迷宫
java·程序人生·算法
·present·21 小时前
CSAPP(lecture 4_01)
c语言·c++
FPGA小迷弟1 天前
用 Claude Code 重构遗留系统:从评估到落地的完整实践指南
c++·python·ai·plotly·django·ai编程·claude
秋天的怀念_David1 天前
算法一千零一夜 · 第十四夜 幻方与奇妙的二进制
java·程序人生·算法
薛定e的猫咪1 天前
(ICLR 2020)MAML 靠的不是快速学习,而是特征复用
人工智能·深度学习·学习·算法·机器学习
程序员-King.1 天前
【ROS1】Noetic 开发工作流程与常用命令
c++·ubuntu·ros
云深麋鹿1 天前
C++ | 基于红黑树实现mymap与myset
开发语言·c++