22. 括号生成

22. 括号生成 - 力扣(LeetCode)

1、一个「合法」括号组合的左括号数量一定等于右括号数量,这个很好理解。

2、对于一个「合法」的括号字符串组合 `p`,必然对于任何 `0 <= i < len(p)` 都有:子串 `p0..i` 中左括号的数量都大于或等于右括号的数量。

思路

明白了合法括号的性质,如何把这道题和回溯算法扯上关系呢?

算法输入一个整数 `n`,让你计算 **`n` 对儿括号**能组成几种合法的括号组合,可以改写成如下问题:

现在有 `2n` 个位置,每个位置可以放置字符 `(` 或者 `)`,组成的所有括号组合中,有多少个是合法的?

这个命题和题目的意思完全是一样的对吧,那么我们先想想如何得到全部 `2^(2n)` 种组合,然后再根据我们刚才总结出的合法括号组合的性质筛选出合法的组合,不就完事儿了?

如何得到所有的组合呢?这就是标准的暴力穷举回溯框架:

cpp 复制代码
result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

那么对于我们的需求,如何打印所有括号组合呢?套一下框架就出来了,伪码如下:

cpp 复制代码
void backtrack(int n, int i, string& track) {
    // i 代表当前的位置,共 2n 个位置
    // 穷举到最后一个位置了,得到一个长度为 2n 组合
    if (i == 2 * n) {
        print(track);
        return;
    }

    // 对于每个位置可以是左括号或者右括号两种选择
    for choice in ['(', ')'] {
        track.push(choice); // 做选择
        // 穷举下一个位置
        backtrack(n, i + 1, track);
        track.pop(choice); // 撤销选择
    }
}

那么,现在能够打印所有括号组合了,如何从它们中筛选出合法的括号组合呢?很简单,加几个条件进行「剪枝」就行了。

对于 `2n` 个位置,必然有 `n` 个左括号,`n` 个右括号,所以我们不是简单的记录穷举位置 `i`,而是用 `left` 记录还可以使用多少个左括号,用 `right` 记录还可以使用多少个右括号,这样就可以通过刚才总结的合法括号规律进行筛选了。

代码

java 复制代码
public List<String> generateParenthesis(int n) {
    if (n == 0) return new ArrayList<>();
    // 记录所有合法的括号组合
    List<String> res = new ArrayList<>();
    // 回溯过程中的路径
    StringBuilder track = new StringBuilder();
    // 可用的左括号和右括号数量初始化为 n
    backtrack(n, n, track, res);
    return res;
}

// 可用的左括号数量为 left 个,可用的右括号数量为 rgiht 个
void backtrack(int left, int right, 
            StringBuilder track, List<String> res) {
    // 若左括号剩下的多,说明不合法
    if (right < left) return;
    // 数量小于 0 肯定是不合法的
    if (left < 0 || right < 0) return;
    // 当所有括号都恰好用完时,得到一个合法的括号组合
    if (left == 0 && right == 0) {
        res.add(track.toString());
        return;
    }
    
    // 尝试放一个左括号
    track.append('('); // 选择
    backtrack(left - 1, right, track, res);
    track.deleteCharAt(track.length() - 1); // 撤消选择

    // 尝试放一个右括号
    track.append(')'); // 选择
    backtrack(left, right - 1, track, res);
    track.deleteCharAt(track.length() - 1); // 撤消选择
}
相关推荐
文丑颜不良啊21 分钟前
LeetCode2481~2500题目解析
java·leetcode
Flynn Liang1 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
聪明的笨猪猪2 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
愿有所息2 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
秋饼3 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
SL_staff4 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈
SL_staff4 小时前
企业AI落地难?JVS-AI渐进式实践:安全可控、小切口验证、标准化沉淀与规模化复制
java·开源·aigc
武子康4 小时前
驾驶轨迹换个坐标,就能给移动机器人用吗?
人工智能·算法·机器学习
SL_staff5 小时前
JVS数字底座实践:构建企业级统一内容管理平台的技术路径
java·产品
nice_lcj5206 小时前
【从零理解 Spring AI · 12】MCP 的两种角色:做客户端还是做服务端
java·后端·spring·spring ai·ai应用开发