news 2026/2/6 15:32:27

Java版LeetCode热题100之括号生成:回溯算法与卡特兰数的完美结合

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java版LeetCode热题100之括号生成:回溯算法与卡特兰数的完美结合

Java版LeetCode热题100之括号生成:回溯算法与卡特兰数的完美结合

摘要:本文将深入剖析 LeetCode 热题 100 中的经典回溯问题——括号生成(Generate Parentheses)。我们将从暴力法出发,逐步优化到高效的回溯算法,并探讨基于卡特兰数的动态规划解法。文章提供完整可运行的 Java 实现,涵盖时间/空间复杂度分析、常见面试问题、实际应用场景、相关题目推荐等模块,帮助读者不仅掌握解题技巧,更能理解其背后的数学原理与算法思想。


一、原题回顾

题目名称:括号生成
题目编号:LeetCode 22
难度等级:中等

题目描述

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

示例

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

提示

  • 1 <= n <= 8

二、原题分析

本题要求生成所有有效的括号组合,关键在于理解什么是"有效":

  • 数量相等:左括号 ‘(’ 和右括号 ‘)’ 的数量必须相等(各 n 个)
  • 顺序合法:在任何前缀中,左括号的数量不能少于右括号的数量

💡核心观察:这是一个典型的约束满足问题,需要在生成过程中确保括号序列始终有效。

由于n ≤ 8,最大输出数量为第 8 个卡特兰数 C₈ = 1430,属于可枚举范围,适合使用回溯法(Backtracking)


三、答案构思

3.1 暴力法思路

  • 生成所有长度为 2n 的 ‘(’ 和 ‘)’ 序列(共 2²ⁿ 种)
  • 对每个序列验证是否有效
  • 缺点:大量无效序列被生成和验证,效率低下

3.2 回溯法优化思路

在生成过程中实时维护有效性约束

  • 左括号约束:已使用的左括号数量 < n 时,可以添加 ‘(’
  • 右括号约束:已使用的右括号数量 < 左括号数量时,可以添加 ‘)’

这样确保每一步生成的序列都是有效的前缀,避免生成无效序列。

3.3 动态规划思路(卡特兰数)

利用括号序列的递归结构:

  • 任何有效括号序列可表示为(A)B
    • A 是 i 对括号的有效序列
    • B 是 (n-1-i) 对括号的有效序列
  • 通过枚举 i 从 0 到 n-1,递归构建所有可能

四、完整答案(Java 实现)

4.1 暴力法实现

classSolution{publicList<String>generateParenthesis(intn){List<String>result=newArrayList<>();char[]current=newchar[2*n];generateAll(current,0,result);returnresult;}privatevoidgenerateAll(char[]current,intpos,List<String>result){if(pos==current.length){if(isValid(current)){result.add(newString(current));}}else{// 尝试添加左括号current[pos]='(';generateAll(current,pos+1,result);// 尝试添加右括号current[pos]=')';generateAll(current,pos+1,result);}}privatebooleanisValid(char[]sequence){intbalance=0;for(charc:sequence){if(c=='('){balance++;}else{balance--;}// 如果右括号多于左括号,无效if(balance<0){returnfalse;}}// 必须左右括号数量相等returnbalance==0;}}

4.2 回溯法实现(推荐)

classSolution{publicList<String>generateParenthesis(intn){List<String>result=newArrayList<>();backtrack(result,newStringBuilder(),0,0,n);returnresult;}privatevoidbacktrack(List<String>result,StringBuildercurrent,intopen,intclose,intmax){// 结束条件:已生成 2n 个字符if(current.length()==max*2){result.add(current.toString());return;}// 添加左括号的条件:左括号数量未达到上限if(open<max){current.append('(');backtrack(result,current,open+1,close,max);current.deleteCharAt(current.length()-1);// 撤销选择}// 添加右括号的条件:右括号数量小于左括号数量if(close<open){current.append(')');backtrack(result,current,open,close+1,max);current.deleteCharAt(current.length()-1);// 撤销选择}}}

4.3 动态规划实现(记忆化递归)

classSolution{privateMap<Integer,List<String>>cache=newHashMap<>();publicList<String>generateParenthesis(intn){returngenerate(n);}privateList<String>generate(intn){if(cache.containsKey(n)){returncache.get(n);}List<String>result=newArrayList<>();if(n==0){result.add("");}else{// 枚举第一个左括号对应的右括号位置for(inti=0;i<n;i++){// i 对括号在内部,n-1-i 对括号在外部for(Stringleft:generate(i)){for(Stringright:generate(n-1-i)){result.add("("+left+")"+right);}}}}cache.put(n,result);returnresult;}}

五、代码分析

5.1 暴力法分析

优点
  • 思路简单直观
  • 易于理解和实现
缺点
  • 效率极低:生成 2²ⁿ 个序列,但有效序列只有 Cₙ 个
  • 大量冗余计算:对于 n=8,生成 65536 个序列,但只有 1430 个有效
时间复杂度
  • O(2²ⁿ × n):生成 2²ⁿ 个序列,每个验证需要 O(n)

5.2 回溯法分析

核心参数含义
  • open:已使用的左括号数量
  • close:已使用的右括号数量
  • max:目标括号对数 n
约束条件详解
  1. 左括号约束open < max
    • 确保不超过 n 个左括号
  2. 右括号约束close < open
    • 确保右括号不会超过左括号(维持有效性)
递归树示例(n=2)
"" / \ "(" "invalid" / \ "((" "()" / / \ "(((" "(()" "()(" (剪枝) | | "(())" "()()"
  • 剪枝效果:避免了所有以 ‘)’ 开头的无效序列

5.3 动态规划分析

递归结构
  • 任何有效序列 =(A)B
  • A 有 i 对括号,B 有 (n-1-i) 对括号
  • i 从 0 到 n-1 枚举所有可能
记忆化优势
  • 避免重复计算相同的子问题
  • 例如:generate(2) 会被多次调用,缓存后只需计算一次
构建过程示例(n=3)
  • i=0:() + generate(2)()(()), ()()()
  • i=1:(()) + generate(1)(())()
  • i=2:((())) + generate(0)((()))

六、时间复杂度与空间复杂度分析

6.1 暴力法

  • 时间复杂度:O(2²ⁿ × n)
    • 生成 2²ⁿ 个序列
    • 每个序列验证需要 O(n)
  • 空间复杂度:O(n)
    • 递归栈深度为 2n
    • 不考虑结果存储空间

6.2 回溯法

  • 时间复杂度:O(4ⁿ/√n)
    • 这是第 n 个卡特兰数 Cₙ 的渐近表达式
    • Cₙ = (1/(n+1)) × C(2n, n) ≈ 4ⁿ/(n^(3/2)√π)
    • 每个有效序列需要 O(n) 时间复制到结果中
  • 空间复杂度:O(n)
    • 递归栈深度最多 2n
    • StringBuilder 的空间为 O(n)

6.3 动态规划法

  • 时间复杂度:O(4ⁿ/√n)
    • 与回溯法相同,因为都要生成所有有效序列
    • 但常数因子可能更大(字符串拼接开销)
  • 空间复杂度:O(4ⁿ/√n)
    • 需要缓存所有子问题的结果
    • 缓存空间与输出空间同量级

📌性能对比:回溯法 > 动态规划 > 暴力法


七、常见问题解答(FAQ)

Q1: 为什么回溯法比暴力法高效?

:回溯法通过约束条件避免生成无效序列:

  • 暴力法:生成所有 2²ⁿ 个序列,再过滤
  • 回溯法:只生成有效的序列前缀,天然避免无效路径
  • 对于 n=8:暴力法处理 65536 个序列,回溯法只处理 1430 个

Q2: 能否用 BFS 实现?

:可以,但效率不如回溯法:

publicList<String>generateParenthesis(intn){List<String>result=newArrayList<>();Queue<String>queue=newLinkedList<>();queue.offer("");while(!queue.isEmpty()){Stringcurrent=queue.poll();if(current.length()==2*n){result.add(current);continue;}intopen=0,close=0;for(charc:current.toCharArray()){if(c=='(')open++;elseclose++;}if(open<n)queue.offer(current+"(");if(close<open)queue.offer(current+")");}returnresult;}

缺点:内存开销大(需存储所有中间状态)

Q3: 如何按字典序输出?

:回溯法天然按字典序输出:

  • 先尝试添加 ‘(’,再尝试添加 ‘)’
  • ‘(’ 的 ASCII 值小于 ‘)’
  • 所以结果自然按字典序排列

Q4: 如果要求生成恰好 k 对连续括号怎么办?

:需要修改约束条件:

  • 跟踪连续括号的长度
  • 在回溯参数中加入额外状态
  • 但这会显著增加复杂度

八、优化思路

8.1 使用 char[] 替代 StringBuilder

减少对象创建开销:

privatevoidbacktrack(List<String>result,char[]current,intindex,intopen,intclose,intmax){if(index==max*2){result.add(newString(current));return;}if(open<max){current[index]='(';backtrack(result,current,index+1,open+1,close,max);}if(close<open){current[index]=')';backtrack(result,current,index+1,open,close+1,max);}}

调用

char[]current=newchar[2*n];backtrack(result,current,0,0,0,n);

优点:无 append/delete 开销
缺点:代码稍复杂

8.2 预分配结果集大小

计算卡特兰数,预设 ArrayList 容量:

// 卡特兰数计算privateintcatalanNumber(intn){if(n<=1)return1;int[]dp=newint[n+1];dp[0]=dp[1]=1;for(inti=2;i<=n;i++){for(intj=0;j<i;j++){dp[i]+=dp[j]*dp[i-1-j];}}returndp[n];}// 使用List<String>result=newArrayList<>(catalanNumber(n));

避免动态扩容的内存拷贝。

8.3 迭代器模式(按需生成)

对于大数据量场景,可实现 Iterator:

publicclassParenthesesIteratorimplementsIterator<String>{// 内部维护当前状态,next() 返回下一个有效序列// 适用于 n 较大但只需部分结果的场景}

但 n≤8 时收益不大。


九、数据结构与算法基础知识点回顾

9.1 卡特兰数(Catalan Number)

  • 定义:Cₙ = (1/(n+1)) × C(2n, n)
  • 前几项:1, 1, 2, 5, 14, 42, 132, 429, 1430…
  • 应用场景
    • n 对括号的有效组合数
    • n+1 个叶子的满二叉树数量
    • 凸 n+2 边形的三角剖分数
    • 栈的合法操作序列数

9.2 回溯算法 vs 动态规划

特性回溯法动态规划
目的枚举所有解求最优解或解的数量
状态当前路径子问题的解
重叠子问题通常没有有(需记忆化)
空间复杂度O(解的深度)O(子问题数量)

9.3 有效括号序列的性质

  1. 数量平衡:左右括号数量相等
  2. 前缀性质:任何前缀中左括号 ≥ 右括号
  3. 递归结构:可分解为(A)B形式
  4. 栈验证:可用栈模拟验证过程

9.4 剪枝技术

  • 可行性剪枝:当前路径已不可能有效(如右括号 > 左括号)
  • 本题的剪枝:通过约束条件避免生成无效前缀
  • 效果:将搜索空间从指数级降低到卡特兰数级别

十、面试官提问环节(模拟)

Q1: 请手写括号生成,并解释回溯的约束条件。

:(写出回溯法代码后解释)

  • 我维护两个计数器:open(左括号数量)和close(右括号数量)
  • 左括号约束open < n时才能添加 ‘(’,确保不超过 n 个
  • 右括号约束close < open时才能添加 ‘)’,确保右括号不会超过左括号
  • 这样保证每一步生成的序列都是有效前缀

Q2: 时间复杂度是多少?为什么是卡特兰数?

  • 时间复杂度 O(4ⁿ/√n),这是第 n 个卡特兰数的渐近表达式
  • 原因:有效括号序列的数量就是卡特兰数 Cₙ
  • Cₙ = (1/(n+1)) × C(2n, n) ≈ 4ⁿ/(n^(3/2)√π)
  • 我们必须生成所有 Cₙ 个序列,所以时间复杂度由输出规模决定

Q3: 如果要求生成的括号序列中不能有连续的 “))” 怎么办?

:需要增加状态跟踪:

  • 在回溯参数中加入lastCharconsecutiveClose
  • 修改右括号的添加条件:
    if(close<open&&(current.length()==0||current.charAt(current.length()-1)!=')'||/* 其他条件 */)){// 添加右括号}

Q4: 动态规划解法的空间复杂度为什么更高?

  • 回溯法:只维护当前路径,空间 O(n)
  • 动态规划:需要缓存所有子问题的结果
  • 子问题 generate(i) 的结果数量是 Cᵢ
  • 总缓存空间 = C₀ + C₁ + … + Cₙ ≈ O(Cₙ) = O(4ⁿ/√n)
  • 所以空间复杂度与输出空间同量级

Q5: 如何验证一个括号序列是否有效?

:两种方法:

  1. 计数法(本题使用):
    intbalance=0;for(charc:s.toCharArray()){balance+=(c=='(')?1:-1;if(balance<0)returnfalse;}returnbalance==0;
  2. 栈法
    Stack<Character>stack=newStack<>();for(charc:s.toCharArray()){if(c=='(')stack.push(c);elseif(stack.isEmpty())returnfalse;elsestack.pop();}returnstack.isEmpty();

十一、这道算法题在实际开发中的应用

11.1 编译器开发

  • 语法分析:验证代码中的括号匹配
  • 错误提示:定位不匹配的括号位置
  • 自动补全:根据上下文生成可能的括号组合

11.2 数据库查询构建

  • SQL 查询:WHERE 条件中的括号嵌套
  • 动态查询:根据用户输入生成合法的括号组合
  • 防止 SQL 注入:验证用户输入的括号结构

11.3 数学表达式解析

  • 计算器应用:验证用户输入的数学表达式
  • 公式编辑器:自动生成合法的括号结构
  • 表达式求值:确保运算符优先级正确

11.4 XML/HTML 解析

  • 标签匹配:虽然不是括号,但原理类似
  • 文档验证:检查嵌套结构的合法性
  • 格式化工具:自动添加缺失的闭合标签

11.5 测试用例生成

  • 边界测试:生成各种括号组合测试解析器
  • 模糊测试:结合有效和无效序列测试鲁棒性
  • 性能测试:使用大量有效序列测试处理速度

💡 虽然实际中很少需要生成所有括号组合,但括号匹配验证是许多系统的底层需求。


十二、相关题目推荐

题号题目难度关联点
20有效的括号简单括号验证基础
921使括号有效的最少添加中等括号平衡问题
1249移除无效的括号困难删除最少括号使有效
32最长有效括号困难动态规划/栈
301删除无效的括号困难BFS + 剪枝
678有效的括号字符串中等含通配符 ‘*’
1541平衡括号字符串的最少插入次数中等贪心算法
1963使字符串平衡的最小交换次数中等括号平衡变种
856括号的分数中等递归计算括号分数
1111有效括号的嵌套深度中等分析括号结构

学习路径建议

  1. 掌握本题(生成所有有效组合)
  2. → 20(验证单个序列)
  3. → 921/1541(最少操作使有效)
  4. → 32/1249(最长/删除无效)
  5. → 678(含通配符的复杂情况)

十三、总结与延伸

13.1 核心收获

  • 括号生成是回溯算法的经典应用
  • 约束条件是避免无效搜索的关键
  • 卡特兰数揭示了有效括号组合的数学本质
  • 三种解法各有适用场景:暴力法(教学)、回溯法(实用)、DP(理论)

13.2 延伸思考

支持多种括号类型?
  • {},[],()三种括号
  • 需要分别跟踪每种括号的平衡
  • 约束条件更复杂,但回溯框架不变
生成特定模式的括号?
  • 如要求最外层必须是()
  • 或要求不能有嵌套深度超过 k
  • 通过修改结束条件和约束条件实现
并行生成?
  • 对第一个决策分支并行处理
  • 如先生成以((开头的所有序列,再生成以()开头的
  • 但 n≤8 时串行已足够快

13.3 学习建议

  1. 理解卡特兰数:掌握其在组合数学中的应用
  2. 熟练回溯模板:路径/选择/约束/结束条件
  3. 区分生成 vs 验证:不同问题用不同方法
  4. 刷相关题:巩固括号问题的各种变种
  5. 思考实际应用:如何将算法思想应用到工程中

结语:括号生成问题看似简单,却蕴含着深刻的算法思想和数学原理。通过这道题,我们不仅学会了回溯算法的应用,还接触到了卡特兰数这一重要的组合数学概念。掌握它,不仅是为了解决这一道题,更是为了建立起解决更复杂约束满足问题的能力。希望本文能助你在算法之路上走得更远!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/5 12:36:44

Windows 11时钟终极美化指南:ElevenClock让你的桌面焕然一新

Windows 11时钟终极美化指南&#xff1a;ElevenClock让你的桌面焕然一新 【免费下载链接】ElevenClock ElevenClock: Customize Windows 11 taskbar clock 项目地址: https://gitcode.com/gh_mirrors/el/ElevenClock 还在为Windows 11任务栏时钟的单调乏味而烦恼吗&…

作者头像 李华
网站建设 2026/1/30 8:42:27

Qwen3双模式大模型:235B参数实现智能推理自由切换

Qwen3双模式大模型&#xff1a;235B参数实现智能推理自由切换 【免费下载链接】Qwen3-235B-A22B-MLX-6bit 项目地址: https://ai.gitcode.com/hf_mirrors/Qwen/Qwen3-235B-A22B-MLX-6bit 导语 Qwen3系列最新发布的2350亿参数大模型Qwen3-235B-A22B-MLX-6bit&#xff0…

作者头像 李华
网站建设 2026/2/4 21:27:51

UniHacker破解工具:解锁Unity全版本专业功能完全指南

UniHacker破解工具&#xff1a;解锁Unity全版本专业功能完全指南 【免费下载链接】UniHacker 为Windows、MacOS、Linux和Docker修补所有版本的Unity3D和UnityHub 项目地址: https://gitcode.com/GitHub_Trending/un/UniHacker 还在为Unity高昂的许可证费用发愁吗&#x…

作者头像 李华
网站建设 2026/2/2 1:43:57

揭秘Cap:这款开源录屏神器如何让你轻松成为视频创作达人

揭秘Cap&#xff1a;这款开源录屏神器如何让你轻松成为视频创作达人 【免费下载链接】Cap Effortless, instant screen sharing. Open-source and cross-platform. 项目地址: https://gitcode.com/GitHub_Trending/cap1/Cap 你知道吗&#xff1f;在视频内容创作日益重要…

作者头像 李华
网站建设 2026/2/2 21:18:12

B站缓存视频极速转换终极指南:轻松突破播放限制

B站缓存视频极速转换终极指南&#xff1a;轻松突破播放限制 【免费下载链接】m4s-converter 将bilibili缓存的m4s转成mp4(读PC端缓存目录) 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 还在为B站缓存视频无法在其他设备播放而苦恼吗&#xff1f;那些精心…

作者头像 李华