一、题目
给你一个只包含 '('
和 ')'
的字符串,找出最长有效(格式正确且连续)括号子串的长度。
二、解题思路
1. 初始化一个栈和一个变量 maxLen
来记录最长有效括号子串的长度。栈用于存储左括号的索引,maxLen
初始化为 0。
2. 遍历字符串 s
中的每个字符。对于每个字符,执行以下操作:
(1)如果当前字符是左括号 '('
,则将其索引压入栈中。
(2)如果当前字符是右括号 ')'
,则执行以下步骤:
- 如果栈不为空,并且栈顶元素是左括号索引,说明找到了一个匹配的括号对。此时,弹出栈顶元素(左括号索引),并计算当前右括号索引与栈顶左括号索引之间的距离(即有效括号子串的长度)。
- 更新
maxLen
为当前maxLen
和刚刚计算的有效括号子串长度的最大值。 - 如果栈为空,说明当前的右括号没有匹配的左括号,将当前索引作为新的左括号压入栈。
3. 遍历结束后,返回 maxLen
作为最长有效括号子串的长度。
这个思路的核心在于利用栈来跟踪左括号的位置,并通过栈顶元素与当前右括号索引之间的距离来确定有效括号子串的长度。这种方法不需要额外的动态规划数组,只需要一个栈和一些基本的索引操作。
三、具体代码
class Solution {
public int longestValidParentheses(String s) {
int maxLen = 0;
int n = s.length();
if (n == 0) return 0;
Stack<Integer> stack = new Stack<>();
stack.push(-1); // 初始化栈,-1 表示没有匹配的左括号
for (int i = 0; i < n; i++) {
if (s.charAt(i) == '(') {
stack.push(i); // 遇到左括号,压入栈
} else {
// 遇到右括号
stack.pop(); // 弹出栈顶元素,即左括号的索引
if (stack.isEmpty()) {
// 如果栈为空,说明当前右括号没有匹配的左括号
stack.push(i); // 将当前索引作为新的左括号压入栈
} else {
// 如果栈不为空,计算当前有效括号的长度
int leftIndex = stack.peek();
maxLen = Math.max(maxLen, i - leftIndex);
}
}
}
return maxLen;
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 时间复杂度:O(n)。
- 代码中有两个循环。
- 外层循环遍历字符串的每个字符,内层循环(实际上是一个条件判断)在遇到右括号时可能会执行。
- 在最坏的情况下,每个字符都会导致内层循环执行,因此时间复杂度是 O(n),其中 n 是字符串的长度。
2. 空间复杂度
- 空间复杂度:O(n)。
- 栈的大小取决于字符串中左括号和右括号的分布。
- 在最坏的情况下,如果字符串中的所有括号都是成对出现的,那么栈的大小将等于字符串的长度。
- 因此,空间复杂度是 O(n)。
五、总结知识点
-
栈(Stack):栈是一种后进先出(LIFO)的数据结构,它用于存储和管理数据。在这个问题中,栈用于跟踪左括号的索引,以便在遇到右括号时能够找到匹配的左括号。
-
字符串遍历:代码通过一个
for
循环遍历字符串的每个字符。这是处理字符串问题时常见的方法。 -
条件判断:在遍历过程中,代码使用
if
和else
语句来判断当前字符是左括号还是右括号,并根据条件执行不同的操作。 -
栈操作:代码中使用了栈的基本操作,包括
push
(压入元素)和pop
(弹出元素)。push
用于将左括号的索引添加到栈中,而pop
用于在找到匹配的右括号时移除左括号的索引。 -
变量更新:在遍历过程中,代码不断更新
maxLen
变量,以记录到目前为止找到的最长有效括号子串的长度。 -
边界条件处理:代码在开始时检查字符串长度是否为 0,如果是,则直接返回 0,因为空字符串没有有效的括号子串。
-
初始化:栈被初始化为包含一个特殊的值
-1
,这个值表示没有匹配的左括号。这是一种常见的技巧,用于简化边界条件的处理。 -
数学运算:代码中使用了
Math.max
方法来获取两个整数的最大值,这是在更新最长有效括号子串长度时需要的操作。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。
标签:子串,maxLen,复杂度,练习,括号,索引,字符串,LeetCode From: https://blog.csdn.net/weixin_62860386/article/details/136427511