力扣解题-20. 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1: 输入:s = "()" 输出:true
示例 2: 输入:s = "()[]{}" 输出:true
示例 3: 输入:s = "(]" 输出:false
提示:
1 <= s.length <= 10⁴
s 仅由括号 '()[]{}' 组成
Related Topics
栈、字符串
第一次解答
解题思路
核心方法:栈(Stack)匹配法,利用栈“先进后出”的特性处理括号的嵌套匹配逻辑,先通过长度奇偶性快速过滤无效用例,再遍历字符串完成括号匹配,时间复杂度O(n)、空间复杂度O(n),逻辑严谨且能完全满足题目约束。
核心逻辑拆解
判断有效括号的核心是“后进先出的嵌套匹配”:
- 前置快速过滤:若字符串长度为奇数,直接返回false(有效括号必成对出现,长度为偶数);
- 栈的核心作用:遇到左括号(
(/{/[)时入栈,遇到右括号时与栈顶左括号匹配; - 匹配规则:
- 遍历字符,若为左括号,压入栈中(记录需要匹配的左括号);
- 若为右括号:
- 若栈为空(无对应的左括号),直接返回false;
- 弹出栈顶左括号,检查是否与当前右括号类型匹配(如
)需匹配(,}需匹配{); - 类型不匹配则返回false;
- 最终校验:遍历完成后,栈必须为空(所有左括号都找到对应右括号匹配),否则返回false。
具体步骤(以s="()[]{}"为例)
- 长度6(偶数),进入遍历;
- i=0(字符
():左括号,入栈 → 栈:[(]; - i=1(字符
)):右括号,栈非空,弹出栈顶(,匹配成功 → 栈:[]; - i=2(字符
[):左括号,入栈 → 栈:[[]; - i=3(字符
]):右括号,弹出栈顶[,匹配成功 → 栈:[]; - i=4(字符
{):左括号,入栈 → 栈:[{]; - i=5(字符
}):右括号,弹出栈顶{,匹配成功 → 栈:[]; - 遍历完成,栈为空,返回true。
性能说明
- 时间复杂度:O(n)(仅遍历字符串一次,栈的入栈/出栈操作均为O(1));
- 空间复杂度:O(n)(最坏情况所有字符都是左括号,栈存储n个元素);
- 优势:
- 前置奇偶校验减少无效遍历(如长度为1的字符串直接返回);
- 匹配失败时立即返回,无需遍历完整字符串;
- 使用
Deque(ArrayDeque)替代Stack,性能更优(Stack是老旧类,基于Vector实现,效率较低)。
if (s.length() % 2 != 0) {
return false;
}
Deque<Character> stack = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
char a = s.charAt(i);
if (a == '(' || a == '{' || a == '[') {
stack.push(a);
} else {
if (stack.isEmpty()) {
return false;
}
char top = stack.pop();
if (a == ')') {
if (top != '(') {
return false;
}
}
if (a == '}') {
if (top != '{') {
return false;
}
}
if (a == ']') {
if (top != '[') {
return false;
}
}
}
}
return stack.isEmpty();
示例解答
解题思路
解法:哈希表优化版(更简洁的匹配逻辑)
核心方法:栈 + 哈希表映射,用HashMap存储“右括号→左括号”的映射关系,替代多分支if判断,代码更简洁且易扩展(如新增括号类型时仅需修改哈希表)。
核心优化点
- 哈希表预存匹配规则:
')'→'(', '}'→'{', ']'→'[',遍历到右括号时直接查表获取目标左括号,减少if分支; - 保持原解法的前置校验和栈逻辑,仅优化匹配判断环节。
代码实现
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
public boolean isValid(String s) {
// 前置校验:奇数长度直接返回false
if (s.length() % 2 != 0) {
return false;
}
// 构建右括号→左括号的映射表
Map<Character, Character> matchMap = new HashMap<>();
matchMap.put(')', '(');
matchMap.put('}', '{');
matchMap.put(']', '[');
Deque<Character> stack = new ArrayDeque<>();
for (char c : s.toCharArray()) {
// 左括号入栈
if (!matchMap.containsKey(c)) {
stack.push(c);
} else {
// 右括号:栈空 或 栈顶不匹配 → 返回false
if (stack.isEmpty() || stack.pop() != matchMap.get(c)) {
return false;
}
}
}
// 所有左括号都匹配完成
return stack.isEmpty();
}
优势说明
- 代码可读性更高:用哈希表替代多层if判断,逻辑更清晰;
- 可扩展性更好:新增括号类型(如
<>)时,仅需在哈希表中添加映射,无需修改逻辑; - 性能与原解法持平:哈希表的
containsKey和get操作均为O(1),无额外性能损耗。
#### 性能说明
- 时间复杂度:O(n²)(最坏情况每次替换仅消除一对括号,需n/2次遍历,每次遍历字符串长度为n);
- 空间复杂度:O(n)(字符串替换生成新对象);
- 适用场景:仅适合理解括号匹配的本质,n=10⁴时会超时,实际工程中不推荐使用。
---
### 总结
1. 基础栈匹配法(第一次解答):逻辑严谨,O(n)时间+O(n)空间,前置奇偶校验提升效率,是核心解法;
2. 哈希表优化版(最优解):代码更简洁、可扩展,性能与基础版持平,工程首选;
3. 关键技巧:
- 核心思想:利用栈的“后进先出”特性处理嵌套匹配;
- 前置优化:奇偶长度校验可快速过滤50%的无效用例;
- 边界处理:右括号匹配时需先检查栈是否为空,避免空栈弹出异常。