使用回溯算法生成有效括号组合的实现详解LeetCode22

1. 问题描述

括号生成问题要求生成所有由 n 对有效括号组成的字符串组合。例如,当 n = 3 时,可能的组合包括 "((()))", "(()())", "()(())", "()()()" 等。有效括号需满足以下条件:

  • 每个左括号必须有对应的右括号。
  • 括号顺序必须正确(例如 ")(" 是无效的)。

2. 回溯算法思想

回溯算法是一种通过递归遍历所有可能解并剪枝无效路径的算法。其核心步骤如下:

  1. 选择:在每一步尝试添加一个左括号或右括号。
  2. 约束:通过条件判断确保括号有效性。
  3. 撤销:当发现当前路径无法生成有效解时,回退到上一步。

3. 解决方案设计

核心思路

  • 左括号优先:先尽可能添加左括号(最多 n 个)。
  • 右括号限制:右括号数量不能超过左括号,避免出现无效组合(如 ")(")。

递归条件

  • 终止条件:当前字符串长度为 2 * n 时,保存结果。
  • 递归路径
    • 若左括号数 < n,添加左括号。
    • 若右括号数 < 左括号数,添加右括号。

4. 完整代码实现

import java.util.ArrayList;
import java.util.List;

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

    private void backtrack(List<String> result, StringBuilder current, 
                           int left, int right, int n) {
        // 终止条件:当前字符串长度为2n
        if (current.length() == 2 * n) {
            result.add(current.toString());
            return;
        }
        
        // 添加左括号的条件:left < n
        if (left < n) {
            current.append("(");
            backtrack(result, current, left + 1, right, n);
            current.deleteCharAt(current.length() - 1); // 回溯
        }
        
        // 添加右括号的条件:right < left
        if (right < left) {
            current.append(")");
            backtrack(result, current, left, right + 1, n);
            current.deleteCharAt(current.length() - 1); // 回溯
        }
    }
}

5. 算法分步解析

关键代码说明

  1. 初始化generateParenthesis 方法初始化结果列表并启动回溯。
  2. 回溯函数参数
    • result:保存所有有效组合。
    • current:当前生成的字符串。
    • left/right:已使用的左/右括号数量。
  3. 终止条件:当字符串长度达到 2n 时,保存结果。
  4. 递归添加括号
    • 左括号:优先添加,直到数量达到 n
    • 右括号:仅在数量小于左括号时添加,确保有效性。
  5. 回溯操作:递归返回时删除最后添加的括号,尝试其他路径。

递归树示意图(以 n=2 为例)

开始("")
├─ 添加 "(" → "(("
│  ├─ 添加 "(" → "(()"
│  │  └─ 添加 ")" → "(())" ✔️
│  └─ 添加 ")" → "()"
│     ├─ 添加 "(" → "()("
│     │  └─ 添加 ")" → "()()" ✔️
├─ 添加 ")" → 无效,跳过

6. 复杂度分析

时间复杂度

  • 卡特兰数模型:有效括号组合数为第 n 个卡特兰数,约为 C(n) = (1/(n+1)) * C(2n, n)
  • 时间复杂度:O(4^n / √n),每个递归步骤有两次选择(左或右)。

空间复杂度

  • 递归栈深度:最大为 2n,因此空间复杂度为 O(n)。

7. 示例与测试

输入输出示例

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

测试代码

public static void main(String[] args) {
    GenerateParentheses solution = new GenerateParentheses();
    List<String> result = solution.generateParenthesis(3);
    System.out.println(result); // 验证输出结果
}

8. 总结与扩展

回溯算法的优势

  • 避免无效搜索:通过条件剪枝减少递归次数。
  • 代码简洁:递归逻辑清晰,易于实现。

扩展思路

  • 动态规划:可预先计算子问题的解并合并结果(如 dp[i] = "(" + dp[j] + ")" + dp[i-j-1])。
  • 迭代法:利用栈或队列模拟递归过程。

通过本文,可以深入理解回溯算法在括号生成问题中的应用,并掌握其实现细节与优化方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的小白菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值