有效的括号
- https://leetcode.cn/problems/valid-parentheses/
描述
-
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
-
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
提示:
- 1 <= s.length <= 10410^4104
- s 仅由括号 ‘()[]{}’ 组成
Typescript 版算法实现
1 )方案 1
function isValid(s: string): boolean {
// 奇数直接不合法
if (s.length % 2 === 1) {
return false;
}
const stack = [];
const leftArr = ['(', '{', '[']
for(let i = 0; i < s.length; i ++) {
// 拿出当前字符
const c = s[i];
// 是左括号进栈
if(leftArr.includes(c)) {
stack.push(c);
} else {
// else 是右括号的情况,拿出栈顶元素(数组最后一位)
const t = stack[stack.length - 1];
// 对比栈顶元素和当前元素是否匹配
const flag1 = t === '(' && c === ')';
const flag2 = t === '{' && c === '}';
const flag3 = t === '[' && c === ']';
if(flag1 || flag2 || flag3) {
// 将栈顶元素出栈
stack.pop();
} else {
// 不匹配,直接退出
return false;
}
}
}
// 字符串扫描完成, 栈应该为空,不为空则false
return stack.length === 0;
};
console.log(isValid('()'));
console.log(isValid("()[]{}"));
console.log(isValid("(]"));
- 解题思路
- 对于没有闭合的左括号而言,越靠后的左括号,对应的右括号越靠前, 如 {[]}
- 满足后进先出,可考虑用栈
- 新建一个栈
- 扫描字符串,遇左括号入栈,遇和栈顶括号类型匹配的右括号就出栈,类型不匹配直接判定为不合法
- 字符串扫描完了,栈是空的就合法,否则不合法
2 )方案 2
function isValid2(s: string): boolean {
// 奇数直接不合法
if (s.length % 2 === 1) {
return false;
}
const stack = [];
// 用字典存储 这种方式缺点是空间复杂度较高
const map = new Map([
['(', ')'],
['{', '}'],
['[', ']']
])
/*
// 这种方式是写起来繁琐
const map = new Map()
map.set('(', ')');
map.set('{', '}');
map.set('[', ']');
*/
for(let i = 0; i < s.length; i++) {
// 拿出当前字符
const c = s[i];
// 是左括号进栈
if(map.has(c)) {
stack.push(c);
} else {
// else 是右括号的情况
// 拿出栈顶元素(数组最后一位)
const t = stack[stack.length - 1];
// 对比栈顶元素和当前元素是否匹配
// 左括号===当前元素
if(map.get(t) === c) {
// 将栈顶元素出栈
stack.pop();
} else {
return false;
}
}
}
// 字符串扫描完成, 栈应该为空,不为空则false
return stack.length === 0;
}
console.log(isValid2('()'));
console.log(isValid2("()[]{}"));
console.log(isValid2("(]"));
- 基于Map这个数据结构,优化了程序的判断方式
3 ) 方案3
function isValid3(s: string): boolean {
const dic: { [key: string]: string } = {'{': '}', '[': ']', '(': ')'};
const stack: string[] = [];
for (const c of s) {
// 注意 in运算符可能会得到原型链上的属性,但是在这里不会出问题,所以可以忽略
if (c in dic) {
stack.push(c);
} else if (dic[stack.pop() as string] !== c) {
return false;
}
}
return stack.length === 0;
}
console.log(isValid3('()'));
console.log(isValid3("()[]{}"));
console.log(isValid3("(]"));
- 这个方案简化了上述两个方案,属于更优的算法
Python3 版算法实现
1 )方案1
class Solution:
def isValid(self, s: str) -> bool:
# 奇数直接不合法
if len(s) % 2 == 1:
return False
stack = []
left_arr = ['(', '{', '[']
for c in s:
# 是左括号进栈
if c in left_arr:
stack.append(c)
else:
# 如果栈为空,直接返回 False
if not stack:
return False
# 是右括号的情况,拿出栈顶元素(列表最后一位)
t = stack[-1]
# 对比栈顶元素和当前元素是否匹配
flag1 = t == '(' and c == ')'
flag2 = t == '{' and c == '}'
flag3 = t == '[' and c == ']'
if flag1 or flag2 or flag3:
# 将栈顶元素出栈
stack.pop()
else:
# 不匹配,直接退出
return False
# 字符串扫描完成,栈应该为空,不为空则返回False
return len(stack) == 0
2 )方案2
class Solution:
def isValid(self, s: str) -> bool:
# 奇数直接不合法
if len(s) % 2 == 1:
return False
stack = []
# 用字典存储
mapping = {
'(': ')',
'{': '}',
'[': ']'
}
for c in s:
# 是左括号进栈
if c in mapping:
stack.append(c)
else:
# else 是右括号的情况
# 拿出栈顶元素(列表最后一位)
if not stack:
return False
t = stack[-1]
# 对比栈顶元素和当前元素是否匹配
if mapping.get(t) == c:
# 将栈顶元素出栈
stack.pop()
else:
return False
# 字符串扫描完成,栈应该为空,不为空则返回 False
return len(stack) == 0
3 )方案3
class Solution:
def isValid(self, s: str) -> bool:
# 字典用于存储左右括号的对应关系
dic = {'{': '}', '[': ']', '(': ')'}
# 用于存储左括号的栈
stack = []
for c in s:
if c in dic:
# 如果是左括号,入栈
stack.append(c)
elif not stack or dic[stack.pop()] != c:
# 如果是右括号,检查栈是否为空,以及是否与栈顶元素匹配
return False
# 最终栈应为空,否则有未匹配的左括号
# return len(stack) == 0 # 这里效果一样
return not stack
Golang 版算法实现
1 )方案1
func isValid(s string) bool {
// 奇数直接不合法
if len(s)%2 == 1 {
return false
}
stack := make([]byte, 0)
leftMap := map[byte]byte{'(': ')', '{': '}', '[': ']'}
for i := 0; i < len(s); i++ {
c := s[i]
// 是左括号进栈
if _, ok := leftMap[c]; ok {
stack = append(stack, c)
} else {
// 如果栈为空,直接返回 False
if len(stack) == 0 {
return false
}
// 是右括号的情况,拿出栈顶元素(切片最后一位)
t := stack[len(stack)-1]
// 当前字符是否匹配栈顶字符
flag1 := t == '(' && c == ')'
flag2 := t == '{' && c == '}'
flag3 := t == '[' && c == ']'
// 不匹配,直接退出
if !(flag1 || flag2 || flag3) {
return false
}
// 匹配,将栈顶元素出栈
stack = stack[:len(stack)-1]
}
}
// 字符串扫描完成,栈应该为空,不为空则返回 False
return len(stack) == 0
}
2 )方案2
func isValid(s string) bool {
// 奇数直接不合法
if len(s)%2 == 1 {
return false
}
stack := make([]byte, 0)
// 用 map 存储
mapping := map[byte]byte{
'(': ')',
'{': '}',
'[': ']',
}
for i := 0; i < len(s); i++ {
// 是左括号进栈
if _, ok := mapping[s[i]]; ok {
stack = append(stack, s[i])
} else {
// else 是右括号的情况
// 拿出栈顶元素(切片最后一位)
if len(stack) == 0 {
return false
}
t := stack[len(stack)-1]
// 对比栈顶元素和当前元素是否匹配
if mapping[t] == s[i] {
// 将栈顶元素出栈
stack = stack[:len(stack)-1]
} else {
return false
}
}
}
// 字符串扫描完成,栈应该为空,不为空则返回 False
return len(stack) == 0
}
3 )方案3
func isValid(s string) bool {
// 使用 map 存储左右括号的对应关系
dic := map[byte]byte{'{': '}', '[': ']', '(': ')'}
// 用于存储左括号的栈
var stack []byte
for i := 0; i < len(s); i++ {
c := s[i]
if _, ok := dic[c]; ok {
// 如果是左括号,入栈
stack = append(stack, c)
} else if len(stack) == 0 || dic[stack[len(stack)-1]] != c {
// 如果是右括号,检查栈是否为空以及是否与栈顶元素匹配
return false
} else {
// 如果匹配,则将栈顶元素出栈
stack = stack[:len(stack)-1]
}
}
// 最终栈应为空,否则有未匹配的左括号
return len(stack) == 0
}
Java 版算法实现
1 )基于栈的括号匹配法(辅助栈法)
import java.util.ArrayDeque;
import java.util.Deque;
class Main1 {
public static boolean isValid(String s) {
// 奇数长度直接不合法
if (s.length() % 2 == 1) {
return false;
}
Deque<Character> stack = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 左括号入栈
if (c == '(' || c == '{' || c == '[') {
stack.push(c);
} else {
// 右括号时,栈若为空则无法匹配
if (stack.isEmpty()) {
return false;
}
char t = stack.peek(); // 栈顶元素
// 检查是否匹配
if ((t == '(' && c == ')') ||
(t == '{' && c == '}') ||
(t == '[' && c == ']')) {
stack.pop(); // 匹配成功,弹出栈顶
} else {
return false; // 不匹配直接失败
}
}
}
// 扫描结束后栈应为空
return stack.isEmpty();
}
// 简单测试
public static void main(String[] args) {
// 示例 1:简单的括号对
System.out.println(isValid("()")); // 输出: true
// 示例 2:多个不同类型的括号对
System.out.println(isValid("()[]{}")); // 输出: true
// 示例 3:不匹配的括号
System.out.println(isValid("(]")); // 输出: false
// 示例 4:嵌套的括号
System.out.println(isValid("({[]})")); // 输出: true
// 示例 5:奇数长度的字符串
System.out.println(isValid("({[})")); // 输出: false
// 示例 6:空字符串
System.out.println(isValid("")); // 输出: true
}
}
算法说明:
- 核心思想:利用栈“后进先出”(LIFO)的特性,将最近遇到的左括号与当前遍历到的右括号进行匹配。
- 提前剪枝:若字符串长度为奇数,括号必然无法完全匹配,可直接返回
false。 - 遍历与入栈:遍历字符串,若当前字符为左括号(
(,{,[),则将其压入栈中等待匹配。 - 匹配与出栈:若当前字符为右括号(
),},]),首先检查栈是否为空。若为空,说明没有对应的左括号,返回false;若不为空,则取出栈顶元素,判断是否与当前右括号匹配。匹配则弹出栈顶元素,不匹配则返回false。 - 最终校验:遍历结束后,检查栈是否为空。若为空,说明所有括号均有效匹配,返回
true;否则说明有左括号未匹配,返回false。
2 )哈希表辅助栈匹配法
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
public class Main2 {
public static boolean isValid(String s) {
// 奇数长度直接不合法
if (s.length() % 2 == 1) {
return false;
}
Deque<Character> stack = new ArrayDeque<>();
// 用 Map 存储括号配对(左 -> 右)
Map<Character, Character> map = new HashMap<>();
map.put('(', ')');
map.put('{', '}');
map.put('[', ']');
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 是左括号则入栈
if (map.containsKey(c)) {
stack.push(c);
} else {
// 右括号时,栈为空则无效
if (stack.isEmpty()) {
return false;
}
// 取出栈顶元素(不弹出)进行匹配
char top = stack.peek();
if (map.get(top) == c) {
stack.pop(); // 匹配成功,弹出栈顶
} else {
return false;
}
}
}
// 扫描完毕,栈应为空
return stack.isEmpty();
}
public static void main(String[] args) {
// 示例 1:简单的括号对
System.out.println(isValid("()")); // 输出: true
// 示例 2:多个不同类型的括号对
System.out.println(isValid("()[]{}")); // 输出: true
// 示例 3:不匹配的括号
System.out.println(isValid("(]")); // 输出: false
// 示例 4:嵌套的括号
System.out.println(isValid("({[]})")); // 输出: true
// 示例 5:奇数长度的字符串
System.out.println(isValid("({[})")); // 输出: false
// 示例 6:空字符串
System.out.println(isValid("")); // 输出: true
}
}
算法说明:
- 核心思想:利用栈的“后进先出”特性处理括号的嵌套关系,同时引入哈希表(Map)存储括号映射,简化条件判断逻辑,使代码更简洁且易于扩展。
- 提前剪枝:若字符串长度为奇数,必然无法完全匹配,直接返回
false。 - 初始化映射:使用
HashMap将左括号作为键,对应的右括号作为值进行存储。 - 遍历与入栈:遍历字符串,若当前字符存在于哈希表的键中(即为左括号),则将其压入栈中。
- 匹配与出栈:若当前字符不在键中(即为右括号),先检查栈是否为空。若为空说明无匹配左括号,返回
false;若不为空,则通过哈希表获取栈顶左括号对应的右括号,判断是否与当前字符相等。相等则弹出栈顶,不相等则返回false。 - 最终校验:遍历结束后,检查栈是否为空。若为空说明全部匹配成功,返回
true;否则返回false。
3 )哈希表与栈结合法(精简版)
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class Main3 {
public static boolean isValid(String s) {
// 括号配对字典,对应 TypeScript 中的 dic
Map<Character, Character> dic = new HashMap<>();
dic.put('{', '}');
dic.put('[', ']');
dic.put('(', ')');
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
// TypeScript 的 in 运算符检查属性是否存在,这里用 containsKey 判断是否为左括号
if (dic.containsKey(c)) {
stack.push(c);
} else {
// 栈为空或栈顶的左括号与当前右括号不匹配时返回 false
// 对应 TS 中 dic[stack.pop()] !== c,但 Java 需先检查栈是否为空
if (stack.isEmpty() || dic.get(stack.pop()) != c) {
return false;
}
}
}
// 栈为空说明所有括号正确闭合
return stack.isEmpty();
}
public static void main(String[] args) {
// 示例 1:简单的括号对
System.out.println(isValid("()")); // 输出: true
// 示例 2:多个不同类型的括号对
System.out.println(isValid("()[]{}")); // 输出: true
// 示例 3:不匹配的括号
System.out.println(isValid("(]")); // 输出: false
// 示例 4:嵌套的括号
System.out.println(isValid("({[]})")); // 输出: true
// 示例 5:奇数长度的字符串
System.out.println(isValid("({[})")); // 输出: false
// 示例 6:空字符串
System.out.println(isValid("")); // 输出: true
}
}
算法说明:
- 核心思想:利用栈处理括号的嵌套关系,同时使用哈希表(字典)建立左右括号的映射,使匹配逻辑更加紧凑。
- 初始化映射:使用
HashMap将左括号作为键,对应的右括号作为值进行存储。 - 遍历与入栈:使用增强 for 循环遍历字符数组,若当前字符存在于哈希表的键中(即为左括号),则将其压入栈中。
- 精简匹配逻辑:若当前字符不在键中(即为右括号),利用逻辑或(
||)的短路特性,在同一个条件语句中先检查栈是否为空;若不为空,则弹出栈顶元素并判断其对应的右括号是否与当前字符相等。若栈空或不匹配,直接返回false。 - 最终校验:遍历结束后,检查栈是否为空。若为空说明所有括号均正确闭合,返回
true;否则返回false。
376



被折叠的 条评论
为什么被折叠?



