题目描述
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
**输入:**s = "()"
**输出:**true
示例 2:
**输入:**s = "()[]{}"
**输出:**true
示例 3:
**输入:**s = "(]"
**输出:**false
示例 4:
**输入:**s = "([])"
**输出:**true
示例 5:
**输入:**s = "([)]"
**输出:**false
提示:
1 <= s.length <= 104s仅由括号'()[]{}'组成
思路解析
一、题目到底在检查什么?
给你一个字符串,只包含:
( ) { } [ ]
问你一句话:
括号是否"合法配对 + 正确嵌套"
合法的例子
() ()[]{} {[()]}
不合法的例子
(] ([)] (((
二、什么叫「合法」?
合法 ≠ 数量相等
合法 = 三条规则同时满足
规则 1:左括号必须被右括号关闭
(
不合法(没关)
规则 2:右括号必须匹配同类型的左括号
(]
类型不对
规则 3:括号必须 后开先关(嵌套顺序)
([)]
你开的是:
( → [
你关的时候却是:
) → ]
顺序错了
三、为什么「栈」是唯一正确模型?
括号的本质是「最近的要先关」
例子:
{ [ ( ) ] }
执行顺序:
-
开
{ -
开
[ -
开
( -
关
)→ 只能关最近的( -
关
]→ 只能关[ -
关
}→ 只能关{
这就是一句话:
后进先出(LIFO)
而 栈 的特性就是:
后进先出
如果不用栈,会发生什么?
比如用变量记录?
-
你最多记一个
-
一旦嵌套多层就崩了
所以 栈是唯一自然模型
四、整体算法思路
从左到右扫描字符串
遇到左括号:压栈
遇到右括号:
看栈顶是不是对应的左括号
是 → 弹出
不是 → 立刻非法
最后:栈必须为空
代码实现
java
class Solution {
/**
* 判断一个只包含 () {} [] 的字符串是否是「有效括号」
*/
public boolean isValid(String s) {
// ① 如果字符串长度是奇数:
// 括号一定无法成对出现,直接返回 false(提前剪枝)
if (s.length() % 2 == 1) {
return false;
}
// 字符串总长度
int n = s.length();
// ② 建立「右括号 -> 左括号」的映射关系
// 作用:当遇到右括号时,能立刻知道它期望匹配的左括号是什么
Map<Character, Character> mp = new HashMap<>();
mp.put(')', '(');
mp.put('}', '{');
mp.put(']', '[');
// ③ 使用栈(Deque)来存放尚未被匹配的左括号
// 栈的特点是:后进先出,正好符合括号"后开的先关"的规则
Deque<Character> stack = new LinkedList<>();
// ④ 从左到右遍历字符串中的每一个字符
for (int i = 0; i < n; i++) {
// 取出当前字符
char ch = s.charAt(i);
// ⑤ 如果当前字符是右括号
// (因为 mp 中只存了右括号作为 key)
if (mp.containsKey(ch)) {
// ⑥ 出现右括号时,必须满足两个条件:
// 1)栈不能是空的(否则说明没有对应的左括号)
// 2)栈顶元素必须是这个右括号对应的左括号
if (stack.isEmpty() || stack.peek() != mp.get(ch)) {
// 只要有一个条件不满足,说明括号不合法
return false;
} else {
// ⑦ 如果匹配成功:
// 当前这一对括号合法,弹出栈顶左括号
stack.pop();
}
} else {
// ⑧ 如果当前字符是左括号:
// 无法立即判断是否合法,只能先压入栈中
// 等后续遇到右括号时再进行匹配
stack.push(ch);
}
}
// ⑨ 遍历结束后:
// 如果栈为空,说明所有左括号都被正确匹配
// 如果栈不为空,说明存在多余的左括号,字符串不合法
return stack.isEmpty();
}
}