题目
标题和出处
标题:有效的括号
出处:20. 有效的括号
难度
3 级
题目描述
要求
给定一个只包括 ‘(’ \texttt{`('} ‘(’, ‘)’ \texttt{`)'} ‘)’, ‘[’ \texttt{`['} ‘[’, ‘]’ \texttt{`]'} ‘]’, ‘{’ \texttt{`\{'} ‘{’, ‘}’ \texttt{`\}'} ‘}’ 的字符串 s \texttt{s} s,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合;
- 左括号必须以正确的顺序闭合。
示例
示例 1:
输入:
s
=
"()"
\texttt{s = "()"}
s = "()"
输出:
true
\texttt{true}
true
示例 2:
输入:
s
=
"()[]{}"
\texttt{s = "()[]\{\}"}
s = "()[]{}"
输出:
true
\texttt{true}
true
示例 3:
输入:
s
=
"(]"
\texttt{s = "(]"}
s = "(]"
输出:
false
\texttt{false}
false
示例 4:
输入:
s
=
"([)]"
\texttt{s = "([)]"}
s = "([)]"
输出:
false
\texttt{false}
false
示例 5:
输入:
s
=
"{[]}"
\texttt{s = "\{[]\}"}
s = "{[]}"
输出:
true
\texttt{true}
true
数据范围
- 1 ≤ s.length ≤ 10 4 \texttt{1} \le \texttt{s.length} \le \texttt{10}^\texttt{4} 1≤s.length≤104
- s \texttt{s} s 仅由括号 "()[]{}" \texttt{"()[]\{\}"} "()[]{}" 组成
解法
思路和算法
由于每个左括号都必须用一个相同类型的右括号闭合,因此有效字符串中的左括号数量和右括号数量相同,即有效字符串的长度一定是偶数。可以首先判断字符串 s s s 的长度的奇偶性,如果长度是奇数,则字符串 s s s 一定不是有效字符串,返回 false \text{false} false。
当字符串 s s s 的长度是偶数时,需要遍历字符串 s s s 判断其是否有效。根据有效字符串的定义,每个左括号必须用相同类型的右括号闭合,且后遇到的左括号必须先闭合,闭合顺序符合栈的「后进先出」的规则,因此可以使用栈判断字符串是否有效。
从左到右遍历字符串 s s s,遇到左括号则将左括号入栈,遇到右括号则尝试用该右括号闭合栈顶的左括号,如果栈不为空且栈顶的左括号和当前的右括号是相同的类型则将栈顶的左括号出栈,如果栈为空或者栈顶的左括号和当前的右括号是不同的类型则返回 false \text{false} false。遍历结束时,如果栈为空,则返回 true \text{true} true,否则返回 false \text{false} false。
上述解法的正确性说明如下。
-
每次将左括号入栈时,栈顶的左括号一定是最后一个没有闭合的左括号。当遇到右括号时,右括号必须闭合最后一个没有闭合的左括号,因此右括号必须闭合栈顶的左括号。只有当栈不为空且栈顶的左括号和当前的右括号是相同的类型时,右括号才能闭合最后一个没有闭合的左括号,否则无法闭合,不是有效的字符串。当右括号闭合栈顶的左括号之后,将闭合的左括号出栈,保证栈内的左括号都是没有闭合的左括号。
-
遍历结束时,如果是有效的字符串,则每个左括号都用一个相同类型的右括号闭合,因此不存在没有闭合的左括号,此时栈为空。如果栈不为空,则栈内的左括号没有闭合,因此不是有效的字符串。
代码
class Solution {
public boolean isValid(String s) {
int length = s.length();
if (length % 2 != 0) {
return false;
}
Deque<Character> stack = new ArrayDeque<Character>();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (isLeftBracket(c)) {
stack.push(c);
} else {
if (stack.isEmpty() || stack.peek() != getComplement(c)) {
return false;
}
stack.pop();
}
}
return stack.isEmpty();
}
public boolean isLeftBracket(char c) {
return c == '(' || c == '[' || c == '{';
}
public char getComplement(char c) {
switch (c) {
case ')':
return '(';
case ']':
return '[';
case '}':
return '{';
default:
return ' ';
}
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。当 n n n 是奇数时直接返回 false \text{false} false,时间复杂度是 O ( 1 ) O(1) O(1)。当 n n n 是偶数时,需要遍历字符串 s s s 一次,每个字符的操作时间都是 O ( 1 ) O(1) O(1),总时间复杂度是 O ( n ) O(n) O(n)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。空间复杂度主要取决于栈空间,栈内的元素个数为 O ( n ) O(n) O(n)。