栈应用:括号匹配
看完你能从零写出括号匹配器,并说清新括号何时入栈、何时该报错
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
栈应用:括号匹配
看完你能从零写出括号匹配器,并说清新括号何时入栈、何时该报错
实例:哪些括号串合法
上一页说了思路:左括号进栈、遇到右括号就弹出配对。可什么样的串才算「配得上」?先别急着写代码,摆几个例子对照看看,合法与非法的差别其实一眼能看出规律。
左括号是叠上盘子,右括号是取最上面那只,类型必须对得上
匹配的规则是什么
上一页我们看了几个例子:合法的串里,被晚写下的左括号,总是被先闭合。这不是巧合——把它们摞在桌面上对照,规律就浮现了:最近未匹配的左括号,最先被闭合。
后洗好的盘子摞在顶上,也最先被取走
第一次尝试:数数行不行
先试最省事的办法:一个计数器,看它能否判对括号串。
构思:栈的对口之处
上一页我们试过数数:只统计左右括号个数,结果分不清「( )」和「) (」。缺的是顺序信息——而顺序恰好是栈最擅长保存的东西:套碗时最后放上去的碗,总是最先被取出来。
扣碗对应左括号进栈,取碗对应右括号找栈顶配对
算法流程图
从左到右扫描,每个字符走一次菱形判断:左括号入栈、右括号找栈顶对口,看三条出口怎么覆盖所有情况。
实现:核心代码
看四步动作如何落到代码:压栈、看栈顶、弹出配对、最后验空。
第5行入栈,第9行 peek 栈顶验证,第10行才弹出,第12行确保左括号不残留——四步对应流程图。
逐步执行:栈状态跟踪
从左到右扫描括号串「([])」,每个箭头是读入一个字符后栈的新状态。
反思:三种失败情形
前面跟踪栈状态时,合法串最后栈恰好清空。但如果括号串不合法呢?栈会在不同时刻、以不同方式暴露问题——把三种失败情形分辨清楚,才算真正吃透这个算法。
取错车位、无车可取、离开时还有车没取,对应三种失败
自测:判一判
用栈判断括号串合法,算法结束时能宣布「合法」的完整条件是什么?
拓展:多种括号混用
多种括号混用时,用栈顶字典判断新括号该配谁,一次比较完成并行配对。
反思:栈思想的普适性
前面我们用栈解决了括号匹配,但值得停下来想想:为什么偏偏是栈?前一页的混用括号已经暗示了答案——嵌套的东西,总是最后打开、最先关闭。
最后套进去的娃最先取出,对应最后出现的左括号最先匹配
要点收束
- ✓数数只看数量,栈才能看住配对方向
- ✓遇左括号入栈,遇右括号找栈顶销账
- ✓结束时栈非空、中途弹空、不匹配都算失败
- ✓扩展到多种括号,只需判断栈顶类型是否对应
- ✓后进先出天然契合就近配对类问题
课后思考
先自己想,再看参考答案——卡住的地方才是收获。
参考答案从不变式想:任何时刻栈里存的都是「尚未等到右括号的左括号」。扫完时栈空、且从未遇到失配,就说明每个左括号都等到了对口右括号。
参考答案可以只用一个整数计数器:见左括号加一,见右括号减一,中途变负或结尾非零即失败。栈的价值在多种括号混用时才真正显现。
参考答案计数器只知道「还差几个」,不知道「差的是哪种」。栈顶恰好记录最近等待的那个左括号,右括号必须先和它配对——后进先出正是嵌套结构的本质。