一、括号补全问题
问题描述
小R有一个括号字符串 s,他想知道这个字符串是否是有效的。一个括号字符串如果满足以下条件之一,则是有效的:
- 它是一个空字符串;
- 它可以写成两个有效字符串的连接形式,即
AB; - 它可以写成
(A)的形式,其中A是有效字符串。
在每次操作中,小R可以在字符串的任意位置插入一个括号。你需要帮小R计算出,最少需要插入多少个括号才能使括号字符串 s 有效。
例如:当 s = "())" 时,小R需要插入一个左括号使字符串有效,结果为 1。
测试样例
样例1:
输入:
s = "())"
输出:1
样例2:
输入:
s = "((("
输出:3
样例3:
输入:
s = "()"
输出:0
样例3:
输入:
s = "()))(("
输出:4
二、解题思路
- 使用栈:栈可以帮助我们追踪未匹配的左括号。
- 遍历字符串:遍历字符串中的每个字符,如果是左括号,则将其压入栈中;如果是右括号,则检查栈是否为空。如果栈为空,说明需要插入一个左括号;如果栈不为空,则弹出一个左括号。
- 计算插入的括号数:遍历结束后,栈中剩余的左括号数量就是需要插入的右括号的数量。
三、solution函数
import java.util.Stack;
public class Main {
public static int solution(String s) {
// 创建一个栈来存储左括号
Stack<Character> stack = new Stack<>();
int count = 0;
for (char c : s.toCharArray()) {
if (c == '(') {
stack.push(c);
} else {
// 如果是右括号且栈为空,说明需要插入一个左括号,`count` 加一
if (stack.isEmpty()) {
count++;
} else {
stack.pop();
}
}
}
// 栈中剩余的左括号数量就是需要插入的右括号的数量
count += stack.size();
return count;
}
public static void main(String[] args) {
System.out.println(solution("()") == 0);
System.out.println(solution("(((") == 3);
System.out.println(solution("()") == 0);
System.out.println(solution("()))((") == 4);
}
}
四、时间和空间复杂度分析
时间复杂度分析
-
遍历字符串: 代码中通过
s.toCharArray()将字符串转换为字符数组,然后用增强for循环遍历每个字符。这一部分的时间复杂度是 O(n),其中 n 是字符串的长度。 -
栈操作: 在循环中,栈的
push和pop操作都是 O(1) 的,因此每次进行栈的操作都是常数时间。综合来看,整个算法的时间复杂度为 O(n)。
空间复杂度分析
-
栈空间: 在最坏情况下,所有字符都是左括号 (,那么栈的最大大小将达到 n。因此,栈的空间复杂度为 O(n)。
-
额外空间: 除了栈,算法只使用了一个 count 整数变量,额外空间复杂度是 O(1)。
因此,整体的空间复杂度是 O(n)。
五、刷后感
这道题的难度相对较低,主要考察了对栈数据结构的理解和运用。在解决问题的过程中,我感受到了以下几点:
- 栈的直观性: 使用栈来处理括号匹配的问题非常自然。每当遇到一个左括号时,就将其入栈,遇到右括号时则尝试出栈,这种后进先出的特性恰好适合解决配对的问题。
- 边界情况处理: 在实现时,特别需要注意一些边界情况,比如字符串为空、全是左括号或全是右括号。这些情况都能通过简单的逻辑判断得到妥善处理。
- 时间与空间复杂度: 通过对时间和空间复杂度的分析,加深了对算法性能的理解。对于涉及到栈的问题,通常时间复杂度都是 O(n),而空间复杂度可能取决于最坏情况下的栈深度,这些都在此题中得到了验证。
- 复习基础知识: 这道题让我温习了栈的基本操作,以及在实际问题中应用数据结构的思维方式,增强了我的编程基础。
总的来说,这道题虽然不难,但却是巩固栈知识的一个很好的练习。