239. 滑动窗口最大值
思路就是代码随想录里的,单调队列,关键是入队时的细节,但是在用Java自带的Deque时,遇到一个问题,一开始,对于单调队列中,pop的实现,我直接调用Deque的pop()方法(本意是想出队),最终导致运行结果不对,看了卡哥代码里用的是poll()方法,这才是出队的方法。其实,我是想用Queue中的出队方法,结果用成了栈中的弹出栈方法(pop)所以导致结果出错,同样的,如果要入队,也应该用add或者offer,而不能用对应栈的入栈方法push!Deque方法我也在我第十天打卡的文章中写了
代码:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
MyQueue myqueue = new MyQueue();
int[] result = new int[nums.length - k + 1];
int count = 0;
for(int i = 0; i < k; i++) { // 先把第一个窗口的数据全部入队
myqueue.push(nums[i]);
}
result[count] = myqueue.getFront(); // 获取第一个窗口的最大值
myqueue.pop(nums[count++]); // 先让第一个数据出队,才可以开始下一次的窗口移动
// 下面开始循环移动窗口
for(int i = k; i < nums.length; i++) {
myqueue.push(nums[i]);
result[count] = myqueue.getFront();
myqueue.pop(nums[count++]);
}
return result;
}
}
class MyQueue {
Deque<Integer> deque = new LinkedList<>();
public void pop(int val) { // 当滑动窗口需要划出的值,刚好也是单调队列的对首值时,才出队
if(!deque.isEmpty() && deque.getFirst() == val) {
deque.poll();
}
}
public void push(int val) { // 入队时,入队的这个值应该一直走到比它大的元素之后,比它小的一律从队尾出队
// 因为,比它小的数字(在它之前),不可能再成为当前这k组数中的最大数,也必然不可能成为下一组中的最大的数,
// 因为队列前面比val小的数,必然要比val先出队,所以在我们自定义的这个“单调队列”中,不再需要维护在队列前,比val小的数
while(!deque.isEmpty() && deque.getLast() < val) {
deque.removeLast();
}
// 之后将val入队,它前面只可能是比它大的数,或者没有
deque.offer(val);
}
public int getFront() {
return deque.getFirst();
}
}
347.前 K 个高频元素
第一次接触,呜呜呜,不太熟悉Java中如何使用优先级队列(大/小顶堆),直接抄代码随想录代码了,但是总结了一下PriorityQueue的常用方法,还有给代码加上了自己的理解注释,加油!
含详细注释的代码:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
for(int num : nums) { // 给数字出现的次数设置map键值对
map.put(num, map.getOrDefault(num,0) + 1);
}
// 设置优先级队列(小顶堆),堆中存放的是一个一维数组,且是一个二元组,数组第一个元素是nums中的数字,第二个元素是数字出现的次数
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2) -> pair1[1] - pair2[1]);
for(Map.Entry<Integer,Integer> entry : map.entrySet()){ // 遍历map
if(pq.size() < k){ // 小于k是直接入队
pq.add(new int[]{entry.getKey(),entry.getValue()});
}else{
if(entry.getValue() > pq.peek()[1]){ // 如果,当前这个元素的值大于堆顶的值,则把堆顶的元素弹出,那么堆中剩下的就是前k个高频的数了(其中,值是指map中的“值”--数字出现的次数,pq.peek()[1],是堆中的数组的第二个元素,也是出现的次数,别忘了堆中存放的是数组,一个二元组,第一个元素存放的是数字,第二个元素存放的是数字出现次数)
pq.poll();
pq.add(new int[]{entry.getKey(),entry.getValue()});
}
}
}
int[] ans = new int[k];
for(int i = k - 1; i >= 0; i--) { // 倒序存入
ans[i] = pq.poll()[0]; // 对顶对应的数字出现次数最少(小顶堆)
}
return ans;
}
}
PriorityQueue常用方法总结
- add(E e):将指定的元素插入此队列中。如果成功插入,则返回true;如果队列已满并且不能容纳更多元素,则抛出IllegalStateException。
- offer(E e):将指定的元素插入此队列中。如果成功插入,则返回true;如果队列已满并且不能容纳更多元素,则返回false。
- remove():检索并删除此队列的头部元素,如果此队列为空,则抛出NoSuchElementException。
- poll():检索并删除此队列的头部元素,如果此队列为空,则返回null。
- element():检索但不删除此队列的头部元素,如果此队列为空,则抛出NoSuchElementException。
- peek():检索但不删除此队列的头部元素,如果此队列为空,则返回null。
- size():返回此队列中的元素数量。
- isEmpty():检查此队列是否为空。
- iterator():返回一个迭代器,该迭代器将按升序顺序依次访问此队列中的元素。
- toArray():返回包含此队列中所有元素的数组。
- clear():移除此队列中的所有元素。
有关优先级队列创建时的语法问题:
一开始对下面这段代码是一脸懵逼的:
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2) -> pair1[1] - pair2[1]);
其实这就是一个设置优先级队列为“小顶堆”的创建方式,通过给构造方法传入自定义比较器(有关优先级队列设置比较器的详解,这里转载了一位大佬的讲解)的方式设置。也就是这段代码:
(pair1,pair2) -> pair1[1] - pair2[1]
而上面这段代码就是一段Java8新增的lambda表达式,理解为简单的函数,参数是pair1和pair2,类型省略了与前面的泛型定义的一致,是整型数组int[],方法体是pair1[1] - pair2[1],返回值也是方法体,因为比较简单,return直接省略了,而pair1[1]就是入队的二元组中的第二个元素,是数字出现的次数。详细的写法应该是这样的:
(int[] pair1, int[] pair2)->{
return pair1[1] - pair2[1];
};
总结:lambda就是一种简单的式子,一种让函数作为参数更加简洁的式子。有关更多也转载了一篇大佬的文章