LeetCode 32:最长有效括号(栈解法,真正理解版)

97 阅读2分钟

一、题目要求

给你一个只包含 '('')' 的字符串 s
请你找出 最长连续的、格式正确的括号子串的长度

示例:

输入:")()())"
输出:4
解释:最长有效括号子串是 "()()"

注意关键词:

  • 连续子串(不能跳)
  • 有效括号(左右必须正确匹配)

二、常见误区

很多人第一反应是:

  • 数一共有多少对括号
  • 或者用计数器左右抵消

但这些方法都会忽略一个核心问题:

有效括号必须是“连续区间”

一旦中间断了,前面的就不能算。


三、核心思路:栈 + 下标

这道题的关键不是“匹配括号”,而是:

找到每一段合法括号区间的长度

关键设计思想

  1. 栈中 不存括号字符

  2. 栈中只存 下标

  3. 栈顶始终代表:

    • 当前这段合法括号子串左边最近的断点

为什么一开始要压入 -1

这是整道题的灵魂点。

stack.push(-1);

含义是:

  • 在字符串开始前,放一个“虚拟断点”
  • 这样当合法括号从索引 0 开始时,也能正确算长度

否则 "()" 这种最简单情况都算不出来。


四、完整代码

class Solution {
    public int longestValidParentheses(String s) {
        int maxLen = 0;
        Deque<Integer> stack = new ArrayDeque<>();

        // 关键:先放一个 -1,作为“基准下标”
        stack.push(-1);

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);

            if (c == '(') {
                // 左括号,直接入栈(记录下标)
                stack.push(i);
            } else {
                // 右括号,尝试匹配
                stack.pop();

                if (stack.isEmpty()) {
                    // 没法匹配,当前右括号成为新的断点
                    stack.push(i);
                } else {
                    // 可以匹配,计算当前合法区间长度
                    maxLen = Math.max(maxLen, i - stack.peek());
                }
            }
        }

        return maxLen;
    }
}

五、逐行分析代码在“干什么”

1. 栈的本质作用

Deque<Integer> stack = new ArrayDeque<>();

栈里存的是:

“不能参与当前合法区间的下标”

不是括号本身。


2. 遇到左括号 (

stack.push(i);

含义:

  • 这个左括号可能成为一个匹配的起点
  • 先记录下标,等右括号来配

3. 遇到右括号 )

stack.pop();

先尝试“配对”:

  • 弹出一个左括号(或者断点)

情况一:栈空了

if (stack.isEmpty()) {
    stack.push(i);
}

说明:

  • 当前这个 ) 没有任何左括号可以匹配
  • 它本身就是一个新的“非法断点”
  • 后面的合法子串,只能从它之后开始算

情况二:栈没空

maxLen = Math.max(maxLen, i - stack.peek());

此时:

  • stack.peek() 指向的是

    • 当前合法括号子串左边最近的断点
  • i - stack.peek() 就是

    • 当前这段合法括号的长度

六、用一句话理解整个算法

栈里存的不是括号,而是
“当前合法括号子串左边,最后一个不合法位置的下标”

每当成功匹配一次,就用当前位置减去这个断点,算出长度。


七、复杂度分析

  • 时间复杂度:O(n)
    每个字符最多进栈、出栈一次
  • 空间复杂度:O(n)
    最坏情况下栈存所有左括号