1. 为什么一道“简单题”值得你反复咀嚼
力扣热题100第69题“有效的括号”,几乎是所有刷题人遇到的第一道栈相关题目。题型本身不复杂:给定一个只包含 (、)、{、}、[、] 的字符串,判断字符串是否有效。但就这么一道被标成“简单”的题,我在实际面试别人的时候,反而最爱拿它开场——因为它考察的不只是你会不会写代码,而是你能不能一眼看出“最近匹配”这个模式,并且用栈恰到好处地解决它。
很多人刷题有个误区:觉得简单题一遍过就够了,赶紧去刷难题。但以我带过团队、也参加过不少面试的经验看,有效的括号这道题恰恰是少数能区分“背过题”和“真理解”的试金石。原因有三点:
- 它要求你掌握栈的“后进先出”特性,这是理解函数调用、表达式求值、浏览器历史记录等一系列场景的底层基础。
- 它逼迫你处理边界条件:栈空时能不能 pop?字符串长度为奇数时还值得遍历吗?这些细节往往就是面试现场写挂的地方。
- 它有一堆高度相关的变体题——括号生成、最长有效括号、删除无效括号——全都是面试高频题,而它们的核心思路都建立在这道题之上。
所以我建议你把这道题当成一个“承重墙”来对待,不光要会做,还要能从各个角度把它讲透。这篇内容就会从算法本质、代码实现、常见坑、变体延伸、以及实际刷题策略几个层面展开,希望能让你一次吃透这道题和它背后的栈思想。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心思路拆解:为什么一定是栈
2.1 题目定义的三个有效条件
力扣题目中给出的有效字符串定义有三条,很多人做这道题的时候只记住了第一条,后面两条才是真正容易出问题的地方:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
第一条说的是类型匹配:( 只能配 ),[ 只能配 ],{ 只能配 },你写一个 (] 直接就是错的。第二条说的是顺序:([)] 即使每个括号类型都能配对,但因为顺序不对——( 应该先被 ) 关闭,结果中间被 ] 插队了——所以整体无效。第三条更细节,它其实是“每个右括号都能找到一个合适的左括号”,这决定了字符串 ")(" 虽然左括号和右括号数量相等,但依然无效。
我见过不少人在白板上写解法时,只检查了左右括号数量是否相等,甚至用计数器来做:
python复制# 错误示范:只统计数量
def isValid(s):
return s.count('(') == s.count(')') and s.count('[') == s.count(']') and s.count('{') == s.count('}')
这个解法对 "()" 这种简单输入是对的,但遇到 "([)]" 就会错误地返回 True。因为它在数量层面满足“每个左括号都有右括号”,但根本没考虑顺序。
2.2 栈为什么天然契合这道题
要处理“顺序”和“最近匹配”,栈就是最自然的数据结构。栈的核心特性是后进先出(LIFO,Last In First Out)。你可以把它想象成放在桌面上的一摞盘子:你最后放上去的那个盘子,一定是最先被取走的那个。
在括号匹配的场景里,这句话翻译过来就是:最后一个出现的左括号,一定是第一个被右括号匹配的。举个例子,字符串 "([{}])" 从左到右遍历时,( 出现后,紧接着是 [,再是 {。等到 } 出现时,它必须先匹配 {,因为 { 是“最后放进”的那一个。这正好是栈顶元素。即便嵌套再深,只要按顺序把左括号压入栈中,遇到右括号时去栈顶比对,逻辑上就是天然正确的。
如果用生活化的类比:栈就像一个停车场只有一个出入口。最先开进去的车辆在最里面,想要开出来,必须等后面所有的车先走。括号嵌套也是这个道理,最内层的括号先闭合,最外层的括号最后闭合。
