有效的括号 图解题解
括号嵌套乱成一团,怎么判断它是否合法?一个栈,从左扫到右,答案自然出来。
像叠盘子配锅盖:左括号就是放下一只锅,压在最上面;遇到右括号,就看最上面那口锅的盖子能不能盖上——能盖就把锅取走,不能盖就说明顺序乱了。最后锅架清空才算全部正确配对。后放的锅最先取,天然靠栈来维护「最近未闭合」的状态。
这道题到底在问什么
- 输入
- s = "([]{()}[])"
- 输出
- true(全部正确闭合)
最优解:为什么这么做
一句话答案:LeetCode 20 有效的括号的标准解法是栈:从左到右扫字符串,左括号一律压栈,右括号则检查栈顶是否为同类型左括号——匹配就弹栈,不匹配或栈已空直接判非法,扫完后栈恰好为空才算有效。每个字符至多进出栈一次,时间 O(n)、空间 O(n)。
这道题真正在问什么
给一个只含 ()[]{} 六种字符的字符串,判断括号是否有效。有效要满足两条:每个左括号都被同类型的右括号闭合,且闭合顺序正确——后打开的括号必须先闭合,不能交叉。比如 "([]{()}[])" 有效,而 "([)]" 虽然左右数量都配平,却因为交叉闭合而无效。「顺序」这个约束,正是本题不能靠简单计数解决的原因。
为什么计数器不行,非要用栈
只有一种括号时,确实可以用计数器:遇 ( 加一、遇 ) 减一,过程中不许为负、最后归零即可。但三种括号混在一起,计数器只能保证每种数量配平,管不了嵌套顺序——"([)]" 三种计数全部平衡,却是非法的,因为 ) 来的时候最近还没闭合的是 [,类型对不上。
问题的本质是:一个右括号该配的对象,永远是「最近一个还没被闭合的左括号」。这是典型的后进先出关系——最晚进来的最先被处理——而后进先出正是栈这个数据结构的定义。可以说不是我们选择了栈,是题目的匹配规则长成了栈的形状。
扫描过程保持什么不变量
算法从左到右扫一遍:遇到左括号就压栈;遇到右括号就看栈顶——若栈为空,或栈顶左括号类型对不上,立即返回 false,否则弹出栈顶表示这一对成功闭合。全程保持的不变量是:栈里自底向上,恰好是「已经打开、还没闭合」的左括号,且越靠栈顶越新。
有了这条不变量,每一步的判断都有了依据:右括号来时只需要看栈顶一个元素,因为合法的闭合顺序规定它只能关最新打开的那个括号;栈顶配不上就绝无别的补救可能,直接判非法是安全的。
为什么最后还要检查栈是否为空
扫描中途不报错,不代表字符串有效。比如 "(((",全程没有任何配对失败,但三个左括号一直躺在栈里没人来关——左括号有剩余同样是非法的。所以最后一步必须检查栈空:栈空说明每个左括号都被恰好闭合过一次,返回 true;否则返回 false。
对称的另一头是「右括号多了」:比如 ")(" 开局就来右括号,此时栈是空的,没有任何左括号可配。这就是为什么处理右括号时要先判空再取栈顶——漏了判空,轻则逻辑错误,重则数组越界。
复杂度怎么算,还能怎么写得更顺
时间 O(n):字符串扫一遍,每个字符至多压栈一次、弹栈一次,配对查表是 O(1)。空间 O(n):最坏情况整串都是左括号,栈要装下全部 n 个字符。
实现上有个小技巧:与其建一张「右括号到左括号」的映射表去核对栈顶,也可以在遇到左括号时直接把它对应的右括号压进栈,之后遇到右括号只需和栈顶比是否相等,逻辑更直白。两种写法复杂度相同,选顺手的即可。
▶ 动画逐步走查(共 23 步)——想跟着动画一帧帧对照就展开
- 3记住这套「左括号压栈、右括号配栈顶、最后栈空才合法」,下面每一步都在套它。
- 4从下标 0 开始扫开局:一个空栈。指针从字符串最左边开始,一个字符一个字符往右扫。左括号进栈、右括号来配对。
- 5cur = (读到第 1 个字符 (。它是左括号,按规则要压进栈,等以后的右括号来关它。
- 6push (压栈。把 ( 放到栈顶,栈里现在是 (。它会一直等到对应的 ) 出现才被弹出。
- 7cur = [读到第 2 个字符 [。它是左括号,按规则要压进栈,等以后的右括号来关它。
- 8push [压栈。把 [ 放到栈顶,栈里现在是 ( [。它会一直等到对应的 ] 出现才被弹出。
- 9cur = ]读到第 3 个字符 ]。它是右括号,要去看栈顶那个左括号能不能和它配上。
- 10] 配 [ ✓配对。右括号 ] 需要的左括号是 [,栈顶正好是 [ —— 配上了!弹出栈顶,这一对括号正确闭合。
- 11cur = {读到第 4 个字符 {。它是左括号,按规则要压进栈,等以后的右括号来关它。
- 12push {压栈。把 { 放到栈顶,栈里现在是 ( {。它会一直等到对应的 } 出现才被弹出。
- 13cur = (读到第 5 个字符 (。它是左括号,按规则要压进栈,等以后的右括号来关它。
- 14push (压栈。把 ( 放到栈顶,栈里现在是 ( { (。它会一直等到对应的 ) 出现才被弹出。
- 15cur = )读到第 6 个字符 )。它是右括号,要去看栈顶那个左括号能不能和它配上。
- 16) 配 ( ✓配对。右括号 ) 需要的左括号是 (,栈顶正好是 ( —— 配上了!弹出栈顶,这一对括号正确闭合。
- 17cur = }读到第 7 个字符 }。它是右括号,要去看栈顶那个左括号能不能和它配上。
- 18} 配 { ✓配对。右括号 } 需要的左括号是 {,栈顶正好是 { —— 配上了!弹出栈顶,这一对括号正确闭合。
- 19cur = [读到第 8 个字符 [。它是左括号,按规则要压进栈,等以后的右括号来关它。
- 20push [压栈。把 [ 放到栈顶,栈里现在是 ( [。它会一直等到对应的 ] 出现才被弹出。
- 21cur = ]读到第 9 个字符 ]。它是右括号,要去看栈顶那个左括号能不能和它配上。
- 22] 配 [ ✓配对。右括号 ] 需要的左括号是 [,栈顶正好是 [ —— 配上了!弹出栈顶,这一对括号正确闭合。
- 23cur = )读到第 10 个字符 )。它是右括号,要去看栈顶那个左括号能不能和它配上。
- 24) 配 ( ✓配对。右括号 ) 需要的左括号是 (,栈顶正好是 ( —— 配上了!弹出栈顶,这一对括号正确闭合。
- 25结果 = true扫完整个字符串,栈正好空了——说明每一个左括号都被正确地关掉、没有多余也没有错配。最终结果:合法 true。
⚠️ 容易写错的地方
✗ 错:遇到右括号不先判栈是否为空
✓ 对:先判空栈,再看栈顶
空栈时来个右括号(如 ")")直接非法,不判空会越界取栈顶
✗ 错:只数左右括号数量相等就算合法
✓ 对:必须类型 + 顺序都对
"([)]" 数量相等但交叉闭合,是非法的
✗ 错:扫完忘了检查栈是否为空
✓ 对:最后必须栈空
"(((" 全程没出错但栈没清空,左括号没关完,非法
完整代码(Python / Java / C++)
Python
def isValid(s):
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for ch in s:
if ch in "([{":
stack.append(ch) # 左括号压栈
else:
if not stack or stack[-1] != pairs[ch]:
return False # 空栈/不匹配
stack.pop() # 配对成功弹栈
return not stack # 最后栈空才合法Java
public boolean isValid(String s) {
Deque<Character> stack = new ArrayDeque<>();
for (char ch : s.toCharArray()) {
if (ch == '(') stack.push(')');
else if (ch == '[') stack.push(']');
else if (ch == '{') stack.push('}');
else if (stack.isEmpty() || stack.pop() != ch)
return false; // 空栈/不匹配
}
return stack.isEmpty(); // 栈空才合法
}C++
bool isValid(string s) {
stack<char> st;
for (char ch : s) {
if (ch == '(') st.push(')');
else if (ch == '[') st.push(']');
else if (ch == '{') st.push('}');
else if (st.empty() || st.top() != ch) return false;
else st.pop();
}
return st.empty(); // 栈空才合法
}复杂度
时间
O(n)
每个字符只进栈/出栈一次,扫一遍
空间
O(n)
最坏全是左括号时,栈里要装下 n 个
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 有效的括号 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么用栈而不是计数器?+
单纯计数只能保证数量平衡,无法处理多种类型的交叉,比如 "([)]" 计数平衡却非法。栈记录了「顺序」,能保证后开的先合,这是计数器做不到的。
能不能压「期望的右括号」来简化?+
能,且更优雅:遇到左括号就压它对应的右括号,遇到右括号直接和栈顶比相等即可,省掉配对表(见代码)。
如果还含字母等其它字符呢?+
通常题目保证只有括号;若有其它字符,按题意决定忽略还是非法——一般直接跳过非括号字符即可。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 有效的括号 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。