LeetCode 经典题:“最长有效括号”

探索Java中求解最长有效括号子串的奥秘

在编程的世界里,字符串处理问题总是充满挑战与趣味。今天,我们来深入探讨一道经典的字符串问题——寻找字符串中最长有效括号子串的长度。这不仅是对字符串操作技巧的考验,更是对逻辑思维和数据结构运用能力的一次锻炼。

一、问题描述

给定一个只包含 ‘(’ 和 ‘)’ 的字符串,我们的任务是找出其中最长有效(格式正确且连续)括号子串的长度。例如,对于字符串 “(()())”,最长有效括号子串就是其本身,长度为6;而对于字符串 “(()”,最长有效括号子串为 “()”,长度为2。

二、解题思路与代码实现

解决这个问题,我们可以巧妙地运用栈(Stack)这一数据结构。在Java中,我们可以使用Deque接口结合LinkedList类来模拟栈的操作。下面是详细的代码实现:

class Solution {
    public int longestValidParentheses(String s) {
        int maxans = 0;
        Deque<Integer> stack = new LinkedList<Integer>();
        stack.push(-1);
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                stack.push(i);
            } else {
                stack.pop();
                if (stack.isEmpty()) {
                    stack.push(i);
                } else {
                    maxans = Math.max(maxans, i - stack.peek());
                }
            }
        }
        return maxans;
    }
}
  1. 初始化部分
    • maxans用于记录我们找到的最长有效括号子串的长度,初始值设为0。因为在还未开始处理字符串时,我们还未找到任何有效子串。
    • stack是一个双端队列,这里我们使用LinkedList来实现栈的功能。特别地,我们首先将 -1 压入栈中。这个 -1 起到了一个重要的边界标记作用,它为后续计算有效子串长度提供了便利。
  2. 遍历字符串
    • 遇到左括号 ‘(’:当我们在字符串中遇到左括号时,执行stack.push(i),将当前左括号的索引i压入栈中。这是因为左括号需要等待对应的右括号出现来完成匹配,我们通过记录其索引,方便后续找到与之匹配的右括号。
    • 遇到右括号 ‘)’:当遇到右括号时,首先执行stack.pop()。这一步是尝试将当前右括号与栈顶的左括号进行匹配。如果栈为空,说明当前右括号没有找到与之匹配的左括号,此时执行stack.push(i),将当前右括号的索引i压入栈中,作为新的边界。如果栈不为空,说明找到了匹配的左括号,此时计算当前有效括号子串的长度。计算方式为i - stack.peek(),即当前右括号的索引i减去栈顶元素(最近匹配的左括号的前一个索引)。然后使用Math.max(maxans, i - stack.peek())来更新maxans,确保maxans始终记录着到目前为止找到的最长有效括号子串的长度。
  3. 返回结果
    • 当整个字符串遍历结束后,maxans中存储的值就是我们要找的最长有效括号子串的长度,直接返回maxans即可。

三、代码示例分析

为了更直观地理解代码的运行过程,我们以字符串 “(()” 为例来详细分析。

  1. 初始化阶段
    • maxans为0,stack中只有一个元素 -1。
  2. 遍历过程
    • i = 0,字符为(,执行stack.push(0),此时stack[-1, 0]
    • i = 1,字符为(,执行stack.push(1),此时stack[-1, 0, 1]
    • i = 2,字符为),执行stack.pop()stack变为[-1, 0]。由于栈不为空,计算maxans = Math.max(0, 2 - 0) = 2,此时maxans更新为2。
  3. 最终结果
    • 遍历结束,返回maxans,值为2,即字符串 “(()” 的最长有效括号子串长度为2,最长有效括号子串为 “()”。

再看字符串 “(()())” 的情况:

  1. 初始化阶段
    • 同样,maxans为0,stack中只有 -1。
  2. 遍历过程
    • i = 0,字符为(stack.push(0)stack[-1, 0]
    • i = 1,字符为(stack.push(1)stack[-1, 0, 1]
    • i = 2,字符为)stack.pop()stack变为[-1, 0],计算maxans = Math.max(0, 2 - 0) = 2
    • i = 3,字符为(stack.push(3)stack[-1, 0, 3]
    • i = 4,字符为(stack.push(4)stack[-1, 0, 3, 4]
    • i = 5,字符为)stack.pop()stack变为[-1, 0, 3],计算maxans = Math.max(2, 5 - 3) = 2
    • i = 6,字符为)stack.pop()stack变为[-1, 0],计算maxans = Math.max(2, 6 - 0) = 6
  3. 最终结果
    • 遍历结束,返回maxans,值为6,即字符串 “(()())” 的最长有效括号子串长度为6,最长有效括号子串就是 “(()())” 本身。

四、复杂度分析

  1. 时间复杂度:代码中通过一个for循环遍历字符串一次,时间复杂度为 O ( n ) O(n) O(n),其中 n n n是字符串的长度。因为每个字符只被访问一次,所以时间复杂度与字符串长度成正比。
  2. 空间复杂度:使用了一个栈来辅助计算,栈中最多可能存储 n n n个元素(在极端情况下,如字符串全部是左括号),所以空间复杂度为 O ( n ) O(n) O(n)

通过对这道题目的详细分析,我们不仅掌握了如何使用栈来解决最长有效括号子串问题,还深入理解了代码的执行过程和复杂度。希望这篇博客能帮助大家在字符串处理和算法学习的道路上更进一步。在实际编程中,类似的思路和技巧可以应用到各种复杂的字符串处理场景中,提升代码的效率和质量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值