0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

详解一道高频算法题:括号生成

算法与数据结构 来源:五分钟学算法 2020-06-03 17:19 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

题目描述

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

例如,给出 n = 3,生成结果为:

[ "((()))", "(()())", "(())()", "()(())", "()()()" ] 题目解析

方法一:回溯算法(深度优先遍历)

如果完成一件事情有很多种方法,并且每一种方法分成若干步骤,那多半就可以使用“回溯”算法完成。

“回溯”算法的基本思想是“尝试搜索”,一条路如果走不通(不能得到想要的结果),就回到上一个“路口”,尝试走另一条路。

因此,“回溯”算法的时间复杂度一般不低。如果能提前分析出,走这一条路并不能得到想要的结果,可以跳过这个分支,这一步操作叫“剪枝”。

做“回溯”算法问题的基本套路是:

1、使用题目中给出的示例,画树形结构图,以便分析出递归结构;

一般来说,树形图不用画完,就能够分析出递归结构和解题思路。

2、分析一个结点可以产生枝叶的条件、递归到哪里终止、是否可以剪枝、符合题意的结果在什么地方出现(可能在叶子结点,也可能在中间的结点);

3、完成以上两步以后,就要编写代码实现上述分析的过程,使用代码在画出的树形结构上搜索符合题意的结果。

在树形结构上搜索结果集,使用的方法是执行一次“深度优先遍历”。在遍历的过程中,可能需要使用“状态变量”。

(“广度优先遍历”当然也是可以的,请参考方法二。)

我们以 n = 2 为例,画树形结构图。

题解配图(1)

画图以后,可以分析出的结论:

左右都有可以使用的括号数量,即严格大于 0 的时候,才产生分支;

左边不受右边的限制,它只受自己的约束;

右边除了自己的限制以外,还收到左边的限制,即:右边剩余可以使用的括号数量一定得在严格大于左边剩余的数量的时候,才可以“节外生枝”;

在左边和右边剩余的括号数都等于 0 的时候结算。

参考代码如下:

publicclassSolution{ publicListgenerateParenthesis(intn){ Listres=newArrayList<>(); //特判 if(n==0){ returnres; } //执行深度优先遍历,搜索可能的结果 dfs("",n,n,res); returnres; } /** *@paramcurStr当前递归得到的结果 *@paramleft左括号还有几个没有用掉 *@paramright右边的括号还有几个没有用掉 *@paramres结果集 */ privatevoiddfs(StringcurStr,intleft,intright,Listres){ //因为是递归函数,所以先写递归终止条件 if(left==0&&right==0){ res.add(curStr); return; } //因为每一次尝试,都使用新的字符串变量,所以没有显式的回溯过程 //在递归终止的时候,直接把它添加到结果集即可,与「力扣」第46题、第39题区分 //如果左边还有剩余,继续递归下去 if(left>0){ //拼接上一个左括号,并且剩余的左括号个数减1 dfs(curStr+"(",left-1,right,res); } //什么时候可以用右边?例如,((((((),此时 left < right,         // 不能用等号,因为只有先拼了左括号,才能拼上右括号         if (right >0&&left< right) {             // 拼接上一个右括号,并且剩余的右括号个数减 1             dfs(curStr + ")", left, right - 1, res);         }     } }

如果我们不用减法,使用加法,即 left 表示“左括号还有几个没有用掉”,right 表示“右括号还有几个没有用掉”,可以画出另一棵递归树。

题解配图(2)

参考代码如下:

publicclassSolution{ //方法 2:用加法 publicListgenerateParenthesis(intn){ Listres=newArrayList<>(); //特判 if(n==0){ returnres; } //这里的dfs是隐式回溯 dfs("",0,0,n,res); returnres; } /** *@paramcurStr当前递归得到的结果 *@paramleft左括号用了几个 *@paramright右括号用了几个 *@paramn左括号、右括号一共用几个 *@paramres结果集 */ privatevoiddfs(StringcurStr,intleft,intright,intn,Listres){ //因为是递归函数,所以先写递归终止条件 if(left==n&&right==n){ res.add(curStr); return; } //如果左括号还没凑够,继续凑 if(left< n) {             // 拼接上一个左括号,并且剩余的左括号个数减 1             dfs(curStr + "(", left + 1, right, n, res);         }         // 什么时候可以用右边?例如,((((((),此时 left > right, //不能用等号,因为只有先拼了左括号,才能拼上右括号 if(right< n && left >right){ //拼接上一个右括号,并且剩余的右括号个数减1 dfs(curStr+")",left,right+1,n,res); } } }

方法二:广度优先遍历

还是上面的题解配图(1),使用广度优先遍历,结果集都在最后一层,即叶子结点处得到所有的结果集,编写代码如下。

publicclassSolution{ classNode{ /** *当前得到的字符串 */ privateStringres; /** *剩余左括号数量 */ privateintleft; /** *剩余右括号数量 */ privateintright; publicNode(Stringres,intleft,intright){ this.res=res; this.left=left; this.right=right; } @Override publicStringtoString(){ return"Node{"+ "res='"+res+'''+ ",left="+left+ ",right="+right+ '}'; } } publicListgenerateParenthesis(intn){ Listres=newArrayList<>(); if(n==0){ returnres; } Queuequeue=newLinkedList<>(); queue.offer(newNode("",n,n)); //总共需要拼凑的字符总数是2*n n=2*n; while(n>0){ intsize=queue.size(); for(inti=0;i< size; i++) {                 Node curNode = queue.poll();                 if (curNode.left >0){ queue.offer(newNode(curNode.res+"(",curNode.left-1,curNode.right)); } if(curNode.right>0&&curNode.left< curNode.right) {                     queue.offer(new Node(curNode.res + ")", curNode.left, curNode.right - 1));                 }             }             n--;         }         // 最后一层就是题目要求的结果集         while (!queue.isEmpty()) {             res.add(queue.poll().res);         }         return res;     } } 

方法三:动态规划

第 1 步:定义状态 dp[i]

使用 i 对括号能够生成的组合。

注意:每一个状态都是列表的形式。

第 2 步:状态转移方程:

i 对括号的一个组合,在 i - 1 对括号的基础上得到;

i 对括号的一个组合,一定以左括号 "(" 开始(不一定以 ")" 结尾),为此,我们可以枚举右括号 ")" 的位置,得到所有的组合;

枚举的方式就是枚举左括号 "(" 和右括号 ")" 中间可能的合法的括号对数,而剩下的合法的括号对数在与第一个左括号 "(" 配对的右括号 ")" 的后面,这就用到了以前的状态。

状态转移方程是:

dp[i] = "(" + dp[可能的括号对数] + ")" + dp[剩下的括号对数]

“可能的括号对数” 与 “剩下的括号对数” 之和得为 i,故“可能的括号对数” j 可以从 0 开始,最多不能超过 i, 即 i - 1;

“剩下的括号对数” + j = i - 1,故 “剩下的括号对数” = i - j - 1。

整理得:

dp[i] = "(" + dp[j] + ")" + dp[i- j - 1] , j = 0, 1, ..., i - 1

第 3 步:思考初始状态和输出:

初始状态:因为我们需要 0 对括号这种状态,因此状态数组 dp 从 0 开始,0 个括号当然就是 [""]。
输出:dp[n] 。
这个方法暂且就叫它动态规划,这么用也是很神奇的,它有下面两个特点:

1、自底向上:从小规模问题开始,逐渐得到大规模问题的解集;

2、无后效性:后面的结果的得到,不会影响到前面的结果。

publicclassSolution{ //把结果集保存在动态规划的数组里 publicListgenerateParenthesis(intn){ if(n==0){ returnnewArrayList<>(); } //这里dp数组我们把它变成列表的样子,方便调用而已 List>dp=newArrayList<>(n); Listdp0=newArrayList<>(); dp0.add(""); dp.add(dp0); for(inti=1;i<= n; i++) {             Listcur=newArrayList<>(); for(intj=0;j< i; j++) {                 Liststr1=dp.get(j); Liststr2=dp.get(i-1-j); for(Strings1:str1){ for(Strings2:str2){ //枚举右括号的位置 cur.add("("+s1+")"+s2); } } } dp.add(cur); } returndp.get(n); } }

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4761

    浏览量

    97147
  • 递归
    +关注

    关注

    0

    文章

    29

    浏览量

    9263

原文标题:超详细!详解一道高频算法题:括号生成

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    openDACS 2025 开源EDA与芯片赛项 赛七:基于大模型的生成式原理图设计

    领域,对促进产业高质量发展具有重要意义。本赛项包含7,下面是赛七 基于大模型的生成式原理图设计的介绍。 2. 命题单位及赛Chai
    发表于 11-13 11:49

    不间断电源(UPS):电力保障的“最后一道防线”

    (UninterruptiblePowerSupply,简称UPS)作为电力保障的“最后一道防线”,通过储能装置与智能转换技术,在市电中断时实现零切换时间供电,成为现代社会的“电力守护者”。、UP
    的头像 发表于 10-29 09:02 417次阅读
    不间断电源(UPS):电力保障的“最后<b class='flag-5'>一道</b>防线”

    【精选直播】openDACS 2025 开源EDA与芯片大赛 赛二 赛四 直播宣讲会

    openDACS2025开源EDA与芯片大赛线上宣讲赛二:TestBench生成与验证10月31日(周五)19:30精彩开播|宣讲信息报告题目赛宣讲:TestBench生成与验证宣
    的头像 发表于 10-28 10:08 651次阅读
    【精选直播】openDACS 2025 开源EDA与芯片大赛 赛<b class='flag-5'>题</b>二 赛<b class='flag-5'>题</b>四 直播宣讲会

    电能质量在线监测装置的高频噪声滤波功能有哪些参数可以配置?

    )动态调整,以实现 “精准滤除噪声、完整保留有用信号” 的目标。以下是可配置的核心参数及其工程意义: 、硬件滤波参数(信号采集前端) 硬件滤波是高频噪声抑制的 “第一道防线”,其参数配置直接影响噪声衰减能力与有用信号完整性。
    的头像 发表于 10-15 16:43 230次阅读

    数据滤波算法的具体实现步骤是怎样的?

    高频电磁、瞬时脉冲等),选择适配的滤波算法并落地。以下以电能质量监测中最常用的 IIR 低通滤波(抗高频干扰)、滑动平均滤波(抗瞬时脉冲)、卡尔曼滤波(抗动态波动) 为例,详解具体实
    的头像 发表于 10-10 16:45 429次阅读

    铅酸蓄电池在线监测:为关键基础设施筑牢“最后一道防线”

    在数据中心、通信基站、轨道交通等关键应用场景中,蓄电池组往往是供电系统的“最后一道屏障”。旦蓄电池出现故障,可能导致系统断电、数据丢失甚至设备损坏,带来不可估量的经济损失和安全风险。因此,对蓄电池
    的头像 发表于 09-23 09:31 377次阅读
    铅酸蓄电池在线监测:为关键基础设施筑牢“最后<b class='flag-5'>一道</b>防线”

    小红书:通过商品标签API自动生成内容标签,优化社区推荐算法

    推荐系统的准确性和用户体验。整个过程结构清晰,分为三个步骤:API集成、标签自动生成算法优化。 第步:商品标签API的集成与数据获取 小红书通过开放API接口,实时获取商品的结构化标签数据。这些标签包括品类(如“美妆”或“服
    的头像 发表于 09-10 16:46 710次阅读
    小红书:通过商品标签API自动<b class='flag-5'>生成</b>内容标签,优化社区推荐<b class='flag-5'>算法</b>

    非对称密钥生成和转换规格详解

    Signature Algorithm),是种基于模算数和整数有限域离散对数难题的种公钥密码算法,常用于数字签名和验签,不能用于加解密。 当前支持使用字符串参数和密钥参数两种方式生成
    发表于 09-01 07:50

    顶坚国产防爆手持终端如何成为石化企业安全生产的第一道防线

    顶坚国产防爆手持终端之所以能成为石化企业安全生产的第一道防线,源于其通过防爆设计、功能集成、实时交互与系统协同,从物理安全、功能安全、管理安全、应急安全等维度,覆盖了安全生产的全流程(预防、监测
    的头像 发表于 08-26 10:31 627次阅读
    顶坚国产防爆手持终端如何成为石化企业安全生产的第<b class='flag-5'>一道</b>防线

    信号发生器如何与波束赋形算法配合优化?

    场景中的多径传播、干扰和用户移动性,从而验证和优化波束赋形算法的性能。以下是具体配合优化方法及实施步骤:、信号发生器在波束赋形优化中的核心作用 模拟多径信道环境 功能:生成包含多径时延、角度扩展
    发表于 08-08 14:41

    聚徽制造业专属工业触摸屏:精准控制每一道工序,提升生产精度

    在制造业竞争日益激烈的当下,产品质量与生产效率成为企业立足市场的关键,而生产精度则是保障产品质量的核心要素。制造业专属工业触摸屏凭借其独特的功能与技术优势,深度融入生产的每一道工序,实现对生
    的头像 发表于 05-16 15:50 565次阅读

    高频四通RFID读写器在仓储物流中的应用

    高频四通RFID读写器凭借其卓越的性能优势,正在深刻改变传统仓储物流的管理模式,四通读写器通过多通道协同工作和大规模标签批量读取能力,为仓储物流管理带来了革命性的效率提升和成本优化。
    的头像 发表于 04-27 16:48 583次阅读

    成品电池综合测试仪:电池品质的最后一道把关人

    综合测试仪便成为了电池生产线上的“最后一道把关人”,为电池品质保驾护航。 成品电池综合测试仪的重要性 成品电池综合测试仪,是种集多种测试功能于体的专业设备,能够对电池进行全面的性能测试和评估。从电池的容量、
    的头像 发表于 03-18 14:30 557次阅读

    SVPWM的原理及法则推导和控制算法详解

    ,而且使直流母线电压的利用率有了很大提高,且更易于实现数字化。下面将对该算法进行详细分析阐述。 文章过长,请点击下方可查阅*附件:SVPWM的原理及法则推导和控制算法详解.pdf
    发表于 03-14 14:51

    富士通如何解锁生成式AI红利 从人才进化到业务赋能

    正面临一道必答题:您准备好驾驭这场生产力革命了吗? 生成式AI有望带来巨大的经济价值。然而现实当中,许多企业陷入“工具先行,人才掉队”的困境,采购最贵的AI系统,却因不会提问、不懂调参、不敢创新,让技术投资沦为昂贵的摆设。 这
    的头像 发表于 02-25 17:32 1079次阅读