20230920刷题记录
参考代码随想录来刷的。
关键词:栈、单调队列
1 20. 有效的括号
力扣题目链接
不多bb,细节就是,遇到左括号的时候将对应的右括号入栈,稍微清爽点。
注意,栈在Java中应当用ArrayDeque实现,不要用Stack实现,这个好像效率很低。
将下面代码的Stack替换成ArrayDeque。
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(')
stack.push(')');
else if (s.charAt(i) == '[')
stack.push(']');
else if (s.charAt(i) == '{')
stack.push('}');
else {
if (stack.isEmpty())
return false;
Character pop = stack.pop();
if (pop != s.charAt(i)) {
return false;
}
}
}
return stack.isEmpty();
}
}
2 1047. 删除字符串中的所有相邻重复项
力扣题目链接
注意这道题是说删除两个相邻且相同的字母,
例如
输入:abbbaca
输出:abaca
思想:
用栈可以记住上一个字符,扫描到当前字符的时候,先判断栈顶是否等于当前字符。若相等,则出栈;否则将当前字符入栈。用栈的话最后还要将栈中字符转化成字符串。
用栈实现
class Solution {
public String removeDuplicates(String s) {
ArrayDeque<Character> deque = new ArrayDeque<>();
char ch;
for (int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
if (deque.isEmpty() || deque.peek() != ch) {
deque.push(ch);
} else {
deque.pop();
}
}
String str = "";
//剩余的元素即为不重复的元素
while (!deque.isEmpty()) {
str = deque.pop() + str;
}
return str;
}
}
用StringBuilder实现,使用一个指针指向上一个字符,来判断,可减少最后一步。
class Solution {
public String removeDuplicates(String s) {
// 将 res 当做栈
// 也可以用 StringBuilder 来修改字符串,速度更快
// StringBuilder res = new StringBuilder();
StringBuffer res = new StringBuffer();
// top为 res 的长度
int top = -1;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 当 top > 0,即栈中有字符时,当前字符如果和栈中字符相等,弹出栈顶字符,同时 top--
if (top >= 0 && res.charAt(top) == c) {
res.deleteCharAt(top);
top--;
// 否则,将该字符 入栈,同时top++
} else {
res.append(c);
top++;
}
}
return res.toString();
}
}
3 150. 逆波兰表达式求值
力扣题目链接
不多bb
class Solution {
public int evalRPN(String[] tokens) {
ArrayDeque<Integer> deque = new ArrayDeque<>();
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("+"))
deque.push(deque.pop() + deque.pop());
else if (tokens[i].equals("-")) {
Integer right = deque.pop();
Integer left = deque.pop();
deque.push(left - right);
}
else if (tokens[i].equals("*"))
deque.push(deque.pop() * deque.pop());
else if (tokens[i].equals("/")) {
Integer right = deque.pop();
Integer left = deque.pop();
deque.push(left / right);
} else
deque.push(Integer.parseInt(tokens[i]));
}
return deque.pop();
}
}
4 239. 滑动窗口最大值
力扣题目链接
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
这道题的基本想法就是使用一个队列,该队列中存放的是当前窗口中的所有元素。
理想中,该队列有3个方法,
- 出队
- 入队
- 获取当前队列中的最大值
显然普通的队列无法实现获取当前队列中的最大值这个功能。
对此,引入单调队列。
我也是第一次接触单调队列。
单调队列是指队列中的元素值从 队首 到 队尾 单调(单调增或单调减)。
显然,如果使用单调队列,就不能将单调区间中的所有元素加入到队列中。
为实现获取当前队列中的最大值这个功能,我们可以创建一个单调队列,该队列的队首元素就是最大值。
该队列有3个方法:
- 出队
因为该队列没有存放窗口中的所有元素,队首元素只是当前窗口的最大值,故出队的时候需要判断一下。
该方法可接收一个参数,为上一个窗口中的第一个元素,判断接收的元素是否等于队首元素。
若相等,则将队首元素出队;否则不进行任何操作。 - 入队
因为该队列存放的是潜在的最大值,故在将元素入队的时候需要将所有小于入队元素的队尾元素从队尾移除。 - 获取当前队列中的最大值
显然,返回队首元素即可。
代码如下
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length - k + 1];
MyQueue queue = new MyQueue();
// 初始状态
int i;
for (i = 0; i < k; i++) {
queue.offer(nums[i]);
}
res[i - k] = queue.getMax();
while(i < nums.length) {
queue.poll(nums[i - k]);
queue.offer(nums[i]);
++i;
res[i - k] = queue.getMax();
}
return res;
}
}
class MyQueue {
Deque<Integer> deque;
public MyQueue() {
this.deque = new LinkedList<>();
}
public void poll(int val) {
if (!deque.isEmpty() && deque.peek() == val)
deque.poll();
}
public void offer(int val) {
while (!deque.isEmpty() && val > deque.getLast()) {
deque.removeLast();
}
deque.offer(val);
}
public int getMax() {
return deque.peek();
}
}
今天本来要刷347.前 K 个高频元素的,但是有点绕,每天再刷吧。
这两天被导师安排去接手了一个c++项目,给爷干吐了,完全不会啊,也不想学。fk