有效的括号
问题简介
题目描述
给定一个只包括 '(',')','{','}','[',']' 的字符串 s,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例说明
✅ 示例 1:
- 输入:s = "()"
- 输出:true
✅ 示例 2:
- 输入:s = "()[]{}"
- 输出:true
❌ 示例 3:
- 输入:s = "(]"
- 输出:false
❌ 示例 4:
- 输入:s = "([)]"
- 输出:false
✅ 示例 5:
- 输入:s = "{[]}"
- 输出:true
解题思路
方法一:栈(推荐解法)
💡 核心思想:利用栈的后进先出(LIFO)特性来匹配括号。
步骤详解:
- 初始化栈:创建一个空栈用于存储左括号
- 遍历字符串:逐个检查每个字符
- 遇到左括号:将左括号压入栈中
- 遇到右括号 :
- 如果栈为空,说明没有对应的左括号,返回 false
- 如果栈顶元素与当前右括号不匹配,返回 false
- 如果匹配,弹出栈顶元素
- 最终检查:遍历完成后,如果栈为空,说明所有括号都正确匹配,返回 true;否则返回 false
方法二:替换法(不推荐,仅作了解)
💡 核心思想:不断替换相邻的有效括号对,直到无法替换为止。
步骤:
- 循环查找并替换
"()"、"[]"、"{}"为空字符串 - 如果最终字符串为空,则有效;否则无效
缺点:时间复杂度较高,为 O(n²)
方法三:计数法(仅适用于单一类型括号)
💡 注意 :此方法不适用于本题,因为本题包含三种不同类型的括号,且需要考虑顺序。计数法只能处理单一类型括号的情况。
代码实现
tabs
```java
import java.util.*;
public class Solution {
public boolean isValid(String s) {
// 使用栈来存储左括号
Deque<Character> stack = new ArrayDeque<>();
// 建立括号映射关系
Map<Character, Character> pairs = new HashMap<>();
pairs.put(')', '(');
pairs.put(']', '[');
pairs.put('}', '{');
for (char c : s.toCharArray()) {
if (pairs.containsKey(c)) {
// 当前字符是右括号
if (stack.isEmpty() || stack.peek() != pairs.get(c)) {
return false;
}
stack.pop();
} else {
// 当前字符是左括号
stack.push(c);
}
}
// 栈为空说明所有括号都匹配
return stack.isEmpty();
}
}
go
func isValid(s string) bool {
// 使用切片模拟栈
stack := []rune{}
// 建立括号映射关系
pairs := map[rune]rune{
')': '(',
']': '[',
'}': '{',
}
for _, char := range s {
if left, exists := pairs[char]; exists {
// 当前字符是右括号
if len(stack) == 0 || stack[len(stack)-1] != left {
return false
}
// 弹出栈顶元素
stack = stack[:len(stack)-1]
} else {
// 当前字符是左括号
stack = append(stack, char)
}
}
// 栈为空说明所有括号都匹配
return len(stack) == 0
}
示例演示
让我们通过示例 "([{}])" 来演示算法执行过程:
| 步骤 | 当前字符 | 栈状态 | 操作 |
|---|---|---|---|
| 1 | ( |
[(] |
压入左括号 |
| 2 | [ |
[(, [] |
压入左括号 |
| 3 | { |
[(, [, {] |
压入左括号 |
| 4 | } |
[(, [] |
匹配成功,弹出 { |
| 5 | ] |
[(] |
匹配成功,弹出 [ |
| 6 | ) |
[] |
匹配成功,弹出 ( |
| 结束 | - | [] |
栈为空,返回 true |
答案有效性证明
✅ 正确性证明:
-
充分性:如果字符串有效,算法必然返回 true
- 有效字符串中的每个右括号都能找到对应的左括号
- 栈的操作保证了正确的匹配顺序
- 最终栈必为空
-
必要性:如果算法返回 true,字符串必然有效
- 算法确保每个右括号都与正确的左括号匹配
- 栈为空说明没有多余的左括号
- 所有括号都按正确顺序匹配
-
边界情况处理:
- 空字符串:返回 true(符合题目要求)
- 只有左括号:栈不为空,返回 false
- 只有右括号:栈为空时遇到右括号,返回 false
复杂度分析
| 复杂度类型 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 栈解法 | O(n) | O(n) |
| 替换法 | O(n²) | O(1) |
📌 详细分析:
- 时间复杂度 O(n):只需要遍历字符串一次,每个字符最多进行一次入栈和出栈操作
- 空间复杂度 O(n):最坏情况下(全是左括号),栈需要存储 n 个字符
问题总结
✅ 关键要点:
- 栈是解决括号匹配问题的标准数据结构
- 必须考虑三种不同类型的括号及其匹配关系
- 边界情况处理很重要(空字符串、只有左/右括号等)
- 映射表的使用让代码更清晰易懂
💡 扩展思考:
- 如果增加更多类型的括号,只需在映射表中添加对应关系
- 此解法可以轻松扩展到处理 HTML 标签匹配等问题
- 在实际工程中,这种括号匹配常用于语法解析、表达式验证等场景
❌ 常见错误:
- 忘记检查栈是否为空就直接访问栈顶元素
- 没有正确建立括号的映射关系
- 忽略了最终栈必须为空的条件
github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions