LeetCode 第20题:有效的括号
LeetCode 第20题:有效的括号
题目描述
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
难度
简单
题目链接
https://leetcode.cn/problems/valid-parentheses/
示例
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
示例 4:
输入:s = "([])"
输出:true
提示
- 1 <= s.length <= 10⁴
- s 仅由括号 '()[]{}' 组成
解题思路
方法:栈
这是一道经典的栈应用题目。我们可以使用栈来解决这个问题,具体思路如下:
关键点:
- 遇到左括号就入栈
- 遇到右括号就与栈顶元素匹配
- 最后栈应该为空
具体步骤:
- 创建一个栈来存储左括号
- 遍历字符串:
- 如果是左括号,入栈
- 如果是右括号:
- 如果栈为空,返回false
- 如果与栈顶括号不匹配,返回false
- 如果匹配,弹出栈顶元素
- 最后检查栈是否为空
时间复杂度:O(n),其中n是字符串长度
空间复杂度:O(n),最坏情况下需要存储所有字符
代码实现
C# 实现
public class Solution {
public bool IsValid(string s) {
// 如果字符串长度为奇数,一定不是有效的括号
if (s.Length % 2 == 1) {
return false;
}
// 创建栈来存储左括号
Stack<char> stack = new Stack<char>();
// 遍历字符串
foreach (char c in s) {
// 如果是左括号,入栈
if (c == '(' || c == '[' || c == '{') {
stack.Push(c);
}
// 如果是右括号
else {
// 如果栈为空,说明没有匹配的左括号
if (stack.Count == 0) {
return false;
}
// 获取栈顶元素
char top = stack.Pop();
// 检查是否匹配
if (c == ')' && top != '(' ||
c == ']' && top != '[' ||
c == '}' && top != '{') {
return false;
}
}
}
// 最后检查栈是否为空
return stack.Count == 0;
}
}
优化版本(使用字典)
public class Solution {
public bool IsValid(string s) {
if (s.Length % 2 == 1) {
return false;
}
// 使用字典存储括号对应关系
Dictionary<char, char> pairs = new Dictionary<char, char> {
{')', '('},
{']', '['},
{'}', '{'}
};
Stack<char> stack = new Stack<char>();
foreach (char c in s) {
// 如果是右括号
if (pairs.ContainsKey(c)) {
if (stack.Count == 0 || stack.Pop() != pairs[c]) {
return false;
}
}
// 如果是左括号
else {
stack.Push(c);
}
}
return stack.Count == 0;
}
}
代码详解
基本版本:
- 长度检查:
- 如果字符串长度为奇数,一定不是有效的括号
- 栈的使用:
- 使用Stack
存储左括号 - 遇到右括号时检查匹配关系
- 使用Stack
- 匹配检查:
- 检查右括号是否与栈顶左括号匹配
- 不匹配则返回false
优化版本:
- 使用字典:
- 存储右括号到左括号的映射关系
- 简化匹配逻辑
- 代码更简洁:
- 减少了if-else判断
- 逻辑更清晰
执行结果
基本版本:
- 执行用时:72 ms
- 内存消耗:37.1 MB
优化版本:
- 执行用时:68 ms
- 内存消耗:37.3 MB
总结与反思
- 这是一道经典的栈应用题目:
- 体现了栈的LIFO特性
- 处理嵌套结构的典型案例
- 优化思路:
- 使用字典简化代码
- 提前判断无效情况
- 注意事项:
- 处理栈为空的情况
- 最后检查栈是否清空
- 考虑字符串长度为奇数的情况