一、题目回顾
给定一个只包含以下字符的字符串:
'(' ')' '{' '}' '[' ']'
要求判断字符串是否是 有效的括号序列。
有效的定义是:
- 左括号必须用 相同类型的右括号 闭合
- 左括号必须以 正确的顺序 闭合
示例:
"()" -> true
"()[]{}" -> true
"(]" -> false
"([)]" -> false
"{[]}" -> true
二、这道题到底在考什么?
表面上看是字符串,
但核心问题其实是:
如何判断“后出现的右括号”,能否和“最近的左括号”匹配?
注意关键词:
最近
这意味着一种典型的结构:
- 后进来的左括号
- 必须先被匹配掉
这正好符合一个数据结构的特性:栈(Stack)
三、为什么用栈?直觉解释
我们从左到右遍历字符串:
- 遇到左括号:
暂时还不知道它什么时候会被匹配,先存起来 - 遇到右括号:
必须和 最近一个尚未匹配的左括号 配对
而“最近一个未处理的元素”——
正是栈的使用场景。
一句话总结:
左括号入栈,右括号出栈并校验
四、整体解题思路
整个算法可以拆成四步:
-
使用一个栈存放左括号
-
遍历字符串中的每一个字符
-
左括号直接入栈
-
右括号时:
- 栈为空 → 非法
- 栈顶不匹配 → 非法
-
遍历结束后:
- 栈为空 → 合法
- 栈非空 → 非法
五、完整代码
class Solution {
public boolean isValid(String s) {
if (s.length() == 0) {
return true;
}
Stack<Character> stack = new Stack<>();
for (char ch : s.toCharArray()) {
if (ch == '(' || ch == '[' || ch == '{') {
stack.push(ch);
} else {
if (stack.isEmpty()) {
return false;
}
char c = stack.pop();
if (ch == ')' && c != '(') return false;
if (ch == ']' && c != '[') return false;
if (ch == '}' && c != '{') return false;
}
}
return stack.isEmpty();
}
}
下面按逻辑顺序拆解。
六、为什么一开始要判空字符串?
if (s.length() == 0) {
return true;
}
空字符串没有任何未闭合的括号,
自然是一个有效括号序列。
这一步不是必须,但能让语义更清晰。
七、栈里到底存的是什么?
Stack<Character> stack = new Stack<>();
栈中存放的是:
还没有被匹配的左括号
例如字符串:
"{[("
遍历到当前位置时,栈中内容为:
{ [ (
八、遇到左括号:为什么直接入栈?
if (ch == '(' || ch == '[' || ch == '{') {
stack.push(ch);
}
原因很简单:
- 左括号暂时无法判断是否合法
- 必须等到未来某个右括号出现才能确定
所以策略是:
延迟处理,先保存状态
九、遇到右括号时,为什么要先判空?
if (stack.isEmpty()) {
return false;
}
这是一个非常关键的边界判断。
情况示例:
")"
或者:
"]()"
右括号出现时,
如果栈为空,说明:
没有任何左括号可以与之匹配
这种情况一定是非法的,必须立刻返回 false。
十、为什么一定要 pop 再判断?
char c = stack.pop();
因为规则要求的是:
当前右括号,必须匹配最近的左括号
而栈顶元素,正是最近入栈、尚未匹配的左括号。
十一、逐种括号匹配判断
if (ch == ')' && c != '(') return false;
if (ch == ']' && c != '[') return false;
if (ch == '}' && c != '{') return false;
这一步做的是:
- 类型是否一致
- 顺序是否正确
例如:
"([)]"
执行过程:
- '(' 入栈
- '[' 入栈
- ')' 出现,pop 出 '['
- '[' != '(' → 直接非法
这正是题目要过滤掉的情况。
十二、为什么最后还要判断栈是否为空?
return stack.isEmpty();
考虑这种情况:
"((("
遍历结束后:
- 没有遇到非法情况
- 但仍然有左括号未被匹配
此时栈非空,说明:
有括号没有闭合
所以结果应为 false。
十三、复杂度分析
-
时间复杂度:
O(n)- 每个字符最多入栈、出栈一次
-
空间复杂度:
O(n)- 最坏情况下所有字符都是左括号
十四、总结
这道题的核心不是“括号”,而是:
如何用数据结构,保存「尚未完成的状态」