力扣解题-20. 有效的括号

5 阅读4分钟

力扣解题-20. 有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1: 输入:s = "()" 输出:true

示例 2: 输入:s = "()[]{}" 输出:true

示例 3: 输入:s = "(]" 输出:false

提示:

1 <= s.length <= 10⁴

s 仅由括号 '()[]{}' 组成

Related Topics

栈、字符串


第一次解答

解题思路

核心方法:栈(Stack)匹配法,利用栈“先进后出”的特性处理括号的嵌套匹配逻辑,先通过长度奇偶性快速过滤无效用例,再遍历字符串完成括号匹配,时间复杂度O(n)、空间复杂度O(n),逻辑严谨且能完全满足题目约束。

核心逻辑拆解

判断有效括号的核心是“后进先出的嵌套匹配”:

  1. 前置快速过滤:若字符串长度为奇数,直接返回false(有效括号必成对出现,长度为偶数);
  2. 栈的核心作用:遇到左括号((/{/[)时入栈,遇到右括号时与栈顶左括号匹配;
  3. 匹配规则
    • 遍历字符,若为左括号,压入栈中(记录需要匹配的左括号);
    • 若为右括号:
      • 若栈为空(无对应的左括号),直接返回false;
      • 弹出栈顶左括号,检查是否与当前右括号类型匹配(如)需匹配(}需匹配{);
      • 类型不匹配则返回false;
  4. 最终校验:遍历完成后,栈必须为空(所有左括号都找到对应右括号匹配),否则返回false。
具体步骤(以s="()[]{}"为例)
  1. 长度6(偶数),进入遍历;
  2. i=0(字符():左括号,入栈 → 栈:[(];
  3. i=1(字符)):右括号,栈非空,弹出栈顶(,匹配成功 → 栈:[]
  4. i=2(字符[):左括号,入栈 → 栈:[[]
  5. i=3(字符]):右括号,弹出栈顶[,匹配成功 → 栈:[]
  6. i=4(字符{):左括号,入栈 → 栈:[{]
  7. i=5(字符}):右括号,弹出栈顶{,匹配成功 → 栈:[]
  8. 遍历完成,栈为空,返回true。
性能说明
  • 时间复杂度:O(n)(仅遍历字符串一次,栈的入栈/出栈操作均为O(1));
  • 空间复杂度:O(n)(最坏情况所有字符都是左括号,栈存储n个元素);
  • 优势:
    1. 前置奇偶校验减少无效遍历(如长度为1的字符串直接返回);
    2. 匹配失败时立即返回,无需遍历完整字符串;
    3. 使用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判断,逻辑更清晰;
  • 可扩展性更好:新增括号类型(如<>)时,仅需在哈希表中添加映射,无需修改逻辑;
  • 性能与原解法持平:哈希表的containsKeyget操作均为O(1),无额外性能损耗。

#### 性能说明
- 时间复杂度:O(n²)(最坏情况每次替换仅消除一对括号,需n/2次遍历,每次遍历字符串长度为n);
- 空间复杂度:O(n)(字符串替换生成新对象);
- 适用场景:仅适合理解括号匹配的本质,n=10⁴时会超时,实际工程中不推荐使用。

---

### 总结
1.  基础栈匹配法(第一次解答):逻辑严谨,O(n)时间+O(n)空间,前置奇偶校验提升效率,是核心解法;
2.  哈希表优化版(最优解):代码更简洁、可扩展,性能与基础版持平,工程首选;
3.  关键技巧:
    - 核心思想:利用栈的“后进先出”特性处理嵌套匹配;
    - 前置优化:奇偶长度校验可快速过滤50%的无效用例;
    - 边界处理:右括号匹配时需先检查栈是否为空,避免空栈弹出异常。