括号生成(回溯法详解)

题目描述

给定一个数字 n,代表括号对数,请你设计一个函数,生成所有可能的 有效 括号组合。

示例

示例 1:

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

示例 2:

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

解题思路:回溯法

回溯(Backtracking)是一种 试探搜索 的方法,我们在递归过程中不断尝试构造合法的括号序列,并在不满足条件时回退。

✅ 关键规则:

  1. i == 2n(左右括号总数达到 2n)时,说明形成了一个有效的括号序列,加入结果列表。
  2. 左括号 数量小于 n 时,可以添加 左括号 '('
  3. 右括号 数量必须小于 左括号数量(保证有效性),才能添加 右括号 ')'

代码实现(Java)

class Solution {
    private int n;
    private final List ans = new ArrayList<>();
    private char[] path;

    public List generateParenthesis(int n) {
        this.n = n;
        path = new char[n * 2];
        dfs(0, 0);
        return ans;
    }

    private void dfs(int i, int open) {
        if (i == n * 2) {
            ans.add(new String(path));
            return;
        }
        if (open < n) {  // 可以放左括号
            path[i] = '(';
            dfs(i + 1, open + 1);
        }
        if (i - open < open) {  // 右括号数量必须小于左括号
            path[i] = ')';
            dfs(i + 1, open);
        }
    }
}

优化思路

  1. 使用 StringBuilder 代替 char[]
    StringBuilder 的可变特性可以减少字符串的创建开销,提高效率。

  2. 减少不必要的参数
    open 变量可以用 leftright 分别表示 剩余可用的左括号剩余可用的右括号,更直观。


优化后的 Java 代码:

class Solution {
    private List ans = new ArrayList<>();

    public List generateParenthesis(int n) {
        backtrack(new StringBuilder(), n, n);
        return ans;
    }

    private void backtrack(StringBuilder path, int left, int right) {
        if (left == 0 && right == 0) {  // 左右括号都用完了
            ans.add(path.toString());
            return;
        }
        if (left > 0) {  // 还能放左括号
            path.append('(');
            backtrack(path, left - 1, right);
            path.deleteCharAt(path.length() - 1);  // 回溯
        }
        if (right > left) {  // 右括号数量必须大于左括号
            path.append(')');
            backtrack(path, left, right - 1);
            path.deleteCharAt(path.length() - 1);  // 回溯
        }
    }
}

复杂度分析

  • 时间复杂度O(4^n / sqrt(n))
    由卡塔兰数 C_n = (1 / (n+1)) * C(2n, n) 推导得出。

  • 空间复杂度O(n) (递归深度最多为 2n)。


总结

回溯法 通过递归搜索所有可能的括号组合,确保生成的括号是有效的。
✅ 通过 优化字符串操作减少参数传递,可以提高效率。
理解 leftright 变量 是写出高效代码的关键。

如果觉得有帮助,记得点赞+收藏!你的支持是我持续更新的动力!

你可能感兴趣的:(算法,深度优先,leetcode,数据结构)