一 LC20.有效的括号
题目要求:
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串s
,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
思路分析:
本题要求我们找到成对出现的括号,要求括号左右两边两两对应,因此我们不能简单粗暴的直接计算括号的数量,而是要统计括号出现的规律。我们可以考虑从内层一层层寻找成对的括号,由内向外一直到找完。这时我们就可以借助栈来完成。
我们遍历整个字符串中的字符,如果是左括号就入栈,右括号就出栈,根据栈的特性,出栈的元素一定是最后一个入栈的元素,因此该元素就和右括号构成了“最内层”的括号组。我们验证这两个元素是否匹配,如果不匹配直接返回false,否则则继续遍历。注意当我们遍历结束时,还需要再次验证栈中是否为空(因为我们将左括号存到了栈里,如果存在多个左括号没有匹配的右括号仍然会返回false)。如果栈也为空,说明确认所有元素都已匹配完成,该字符串符合“有效括号”的定义。
完整代码示例:
class Solution {
public boolean isValid(String s) {
// 判断字符串数量如果为奇数,则直接返回false
if(s.length() % 2 == 1 ) {
return false;
}
// 创建一个栈,利用其先进后出的特性,判断对应位置元素是否匹配
Stack<Character> stack = new Stack<>();
// 遍历字符串,如果是左括号,则将对应的右括号入栈
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c == '(') {
stack.push(')');
} else if(c == '{') {
stack.push('}');
} else if(c == '[') {
stack.push(']');
} else{
// 如果是右括号,判断出栈元素是否与该字符相同,不同直接返回false(需要先判空)
if(stack.isEmpty() || c != stack.pop()) {
return false;
}
}
}
// 遍历结束,检查栈中是否存在元素,不存在则匹配了
return stack.isEmpty();
}
}
二 LC155.最小栈
题目要求:
设计一个支持
push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。实现
MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。
思路分析:
构建一个栈,实现出栈入栈方法很简单,但是如何能在常数时间内检索到最小元素呢?我们可以借助另一个辅助栈实现,一个栈用于存储正常的元素,另一个栈存储当前的最小元素,每当有新元素入栈时,判断它与辅助栈的栈顶元素的大小,只有它小于原栈顶元素,才会入栈。
完整代码示例:
class MinStack {
// 主栈存储所有元素
private Stack<Integer> stack;
// 辅助栈存储最小值
private Stack<Integer> minStack;
// 初始化两个栈
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
// 将元素 val 压入栈中
public void push(int val) {
stack.push(val);
// 维护辅助栈,存储当前的最小值
if (minStack.isEmpty() || val <= minStack.peek()) {
minStack.push(val);
}
}
// 弹出栈顶元素
public void pop() {
// 如果弹出的元素是最小值,也需要弹出辅助栈的栈顶
if (stack.peek().equals(minStack.peek())) {
minStack.pop();
}
stack.pop();
}
// 获取栈顶元素
public int top() {
return stack.peek();
}
// 获取栈中的最小值
public int getMin() {
return minStack.peek();
}
}
三 LC394.字符串解码
题目要求:
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为:
k[encoded_string]
,表示其中方括号内部的encoded_string
正好重复k
次。注意k
保证为正整数。你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数
k
,例如不会出现像3a
或2[4]
的输入。
思路分析:
这道题用文字解释不太容易理解,可以参考力扣题解https://leetcode.cn/problems/decode-string/solutions/264879/zhan-de-ji-yi-nei-ceng-de-jie-ma-liao-bie-wang-lia/的思路。
但是该题解只提供了js和go语言版本的代码,这里给出Java版本的。
完整代码示例:
class Solution {
public String decodeString(String s) {
Stack<Integer> countStack = new Stack<>(); // 保存重复次数的栈
Stack<StringBuilder> stringStack = new Stack<>(); // 保存部分字符串的栈
StringBuilder currentString = new StringBuilder(); // 当前正在处理的字符串
int k = 0; // 当前的重复次数
for (char ch : s.toCharArray()) {
if (Character.isDigit(ch)) {
// 如果是数字,构建重复次数
k = k * 10 + (ch - '0');
} else if (ch == '[') {
// 将当前的重复次数和字符串压入栈中
countStack.push(k);
stringStack.push(currentString);
// 开始处理新的部分字符串,重置k和currentString
currentString = new StringBuilder();
k = 0;
} else if (ch == ']') {
// 从栈中弹出重复次数和之前的部分字符串
int repeatTimes = countStack.pop();
StringBuilder decodedString = stringStack.pop();
// 将当前字符串按重复次数拼接到之前的字符串
for (int i = 0; i < repeatTimes; i++) {
decodedString.append(currentString);
}
currentString = decodedString; // 更新当前处理的字符串
} else {
// 普通字符,直接加入当前字符串
currentString.append(ch);
}
}
return currentString.toString(); // 返回解码后的字符串
}
}