当前:LC20 · 有效的括号 · 首次出现于 Day 12 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

LC20 · Valid Parentheses · 栈

有效的括号:栈顶永远是最近未还的债

左括号入栈,右括号只和栈顶对账。后开的必须先关,交叉或种类错当场失败。

从左到右扫 s:左括号入栈;右括号时若栈空或与栈顶种类不同则 false,否则弹栈。扫完栈必须空。时间 O(n),空间 O(n)。

时间 O(n)空间 O(n)结论先行 · 全文约 6 节
导读

给你一个只含 ()[]{} 的字符串,判断括号是否成对、是否按正确顺序闭合。合法必须同时满足三条:种类对得上、后开的先关、每个左括号都被还清。

主例 s = "()[]{}" 返回 true:三对括号彼此不嵌套,一对开完立刻关上。对照例 s = "([)]" 返回 false:圆括号和方括号数量都齐,但交叉了——先开的 ( 还没还,中间的 [ 却被 ) 来对账。

只数数量会把 "([)]" 判成合法。本文要回答的是:为什么「最近未闭合的左括号」必须用栈来记,以及栈顶对不上时,交叉是怎么被当场抓住的。

右括号只配最近未闭合的左括号

把每个左括号想成一笔还没写完的债。后开的债更近,必须先还;先开的债压在下面,等近的还清才能轮到它。这不是口号,是括号嵌套自己长出来的次序:后进先出。栈只暴露一端,栈顶永远指向「现在必须先还的那一笔」。

从左到右扫字符。碰到 (、[、{,事情还没完,压进栈。碰到 )、]、},立刻和栈顶对账:栈空,说明多出来一个右括号,没有左括号可配,直接 false;栈顶种类不对,说明还错了人,也是 false;种类对,弹栈,这笔债还清。扫完之后栈还必须空——栈里剩着左括号,就是有债没还。

先用手走主例 "()[]{}"。一开始栈空。读到 (,入栈,栈是 [(]。读到 ),栈顶是 (,种类对,弹出,栈空。读到 [,入栈;读到 ],配掉 [。读到 {,入栈;读到 },配掉 {。结束栈空,合法。三对彼此不嵌套,栈的深度最多为 1,演示场景里每一步都是「入一个、立刻弹出」。

嵌套才会真正用上栈深。"{[]}" 里先压 {,再压 [,栈变成 [{, [];] 必须先配栈顶的 [,弹出后栈顶才轮到 {,再被 } 配掉。后开的 [ 压在 { 上面,必须先弹。计数法只能看见两种括号各一对,看不见谁包着谁。

再看种类错:"(]"。( 入栈,] 来对账,栈顶是圆括号,对不上方括号,false。左右数量是 1 对 1,只数个数会判对;栈顶比的是种类,会判错。

最后看交叉,这是数量齐也会失败的那一类。s = "([)]": ( 入栈,栈是 [(];[ 入栈,栈是 [(, [];下一个是 ),栈顶却是 [。) 要配的是最近的左括号,最近的是 [,种类不对,当场 false。后面的 ] 根本轮不到看。左右括号各两对,数量完全相等,失败写在「还错了人」,不是写在「还没还完」。

如果误用队列来配,队头是最早的 (,来的第一个右括号是 ),种类对得上,队列会把它配掉——交叉被放行。队列记住的是「最早那笔债」,右括号要配的却是「最近那笔债」。开口方向反了,"([)]" 一定漏检。

还有一种扫完才暴露的失败:"(()"。两个 ( 入栈,一个 ) 只弹掉栈顶,结束时栈里还剩一个 (。全程没有种类错误,失败写在最后那句「栈必须空」。只检查「每次右括号都能配上」会漏掉这笔没还的债。

栈顶即期待最晚进入的左括号需要最早闭合。栈顶不是辅助变量,它就是「现在必须先还的那一笔」。数量齐只能证明债的张数对,不能证明没有还错人——"([)]" 就是铁证。
s = "()[]{}"
([0]
)[1]
[[2]
][3]
{[4]
}[5]
(
读到 (
读到 ( → 入栈

Go:栈 + 配对表

solution.goGo
func isValid(s string) bool {
pair := map[rune]rune{')': '(', ']': '[', '}': '{'}
stack := []rune{}
for _, c := range s {
if c == '(' || c == '[' || c == '{' {
stack = append(stack, c)
continue
}
if len(stack) == 0 || stack[len(stack)-1] != pair[c] {
return false
}
stack = stack[:len(stack)-1]
}
return len(stack) == 0
}

1pair 把右括号映射到它必须配上的左括号:) 要 (,] 要 [,} 要 {。

2左括号一律入栈,相当于把一笔未了的债记在栈顶。

3右括号来了:栈空是多出来的右括号;栈顶不等于 pair[c] 是种类错或交叉。"([)]" 会在 ) 对上栈顶 [ 时走这条 false。

4扫完还要看栈空。"(()" 全程没有种类错误,失败写在最后这一句。

总结

右括号只和栈顶对账;"([)]" 数量齐,但 ) 对不上最近的 [。

  • 合法三条:种类对、后开先关、左括号还清。只数数量过不了 "([)]"。
  • 栈顶就是最近未闭合的左括号。栈空遇右括号、种类不对,都是当场失败。
  • 扫完栈非空,说明还有没还的左括号。队列会配最早的债,交叉会漏检。
同族题目
LC22括号生成LC32最长有效括号LC394字符串解码