#LeetCode239. Sliding Window Maximum
#LeetCode 239. 视频讲解:单调队列正式登场!| LeetCode:239. 滑动窗口最大值_哔哩哔哩_bilibili
单调队列是一种单调递增或者单调递减的队列。在Java 中是通过Deque 接口来实现的,其中部分常用函数有:addFirst(E e), removeFirst(), getFirst(), addLast(E e), removeLast(), getLast() 。
之所以选择单调队列,是因为可以两端添加(称呼为:入口、出口)或者删除元素以及排序,在本题中选择的单调队列是单调递减的队列。
在poll 方法中,需要首先判断队列非空,如果当前值与队列中出口最前端元素相等,则弹出当前元素,此时意味着窗口向后移动了,最左边元素会被舍弃,在最右端加入新的元素。如果不相等,则代表原本最左边的元素已经在添加新元素(add 方法)时被弹出,此时不会弹出最前端元素,因为最前端元素本来就应该属于当前的窗口。
在add 方法中,依然需要判断队列非空,只有非空情况下才存在First 元素和Last 元素。添加元素时是比较最后一个元素与添加的元素之间的关系,如果添加元素大于入口元素数值,则入口元素弹出,直到添加元素小于等于入口元素为止,目的是保证是单调队列。
在主函数的for 循环中,首先调用的是poll 函数,目的是先检查窗口移动时最左侧元素是否已经被弹出。
滑动窗口方法代码:
class MyQueue {
Deque<Integer> deque = new LinkedList<>();
public void poll(int value) {
if (!deque.isEmpty() && value == deque.peek()) {
deque.poll();
}
}
public void add(int value) {
while (!deque.isEmpty() && value > deque.getLast()) {
deque.removeLast();
}
deque.add(value);
}
public int peek() {
return deque.peek();
}
}
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 1) {
return nums;
}
int[] result = new int[nums.length - k + 1];
int number = 0;
MyQueue queue = new MyQueue();
for (int i = 0; i < k; i++) {
queue.add(nums[i]);
}
result[number++] = queue.peek();
for (int i = k; i < nums.length; i++) {
queue.poll(nums[i - k]);
queue.add(nums[i]);
result[number++] = queue.peek();
}
return result;
}
}
#LeetCode347. Top K Frequent Elements
#LeetCode 347. 视频讲解:优先级队列正式登场!大顶堆、小顶堆该怎么用?| LeetCode:347.前 K 个高频元素_哔哩哔哩_bilibili
当看到统计元素的出现概率时,需要想到map 映射是否可以解决,key 表示元素,value 来记录次数,刚好回顾之前复习过的哈希映射。
在这里用到了优先级队列,是在队头取元素,在队尾添加元素。比较器Comparator 是优先级队列中重要接口,优先级队列不是按照插入顺序排列元素,而是按照元素的自然顺序或者根据构造时提供的Comparator 决定的优先级顺序来排列元素。
在统计了出现次数后,选择使用min-heap ,min-heap 是二叉树的一种形式,根节点是小元素,子节点均大于根节点,在队列中根节点始终是优先级最高的元素。如果使用max-heap 形式,在每次移动更新的时候,都把最大的元素弹出去了。
Map.Entry 是内部的一个接口,用于表达Map 的键值对,分别通过getKey() 和getValue() 来访问。pair1[1] - pair2[1] 是Lambda 表达式,代表的是比较的内容是Map 键值对的value 部分,也就是出现的次数。
min-heap 代码:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> frequentMap = new HashMap<>();
for(int num: nums) {
frequentMap.put(num, frequentMap.getOrDefault(num, 0) + 1);
}
Comparator<int[]> arrayComparator = (pair1, pair2) -> pair1[1] - pair2[1];
PriorityQueue<int[]> result = new PriorityQueue<>(arrayComparator);
for (Map.Entry<Integer, Integer> entry: frequentMap.entrySet()) {
if (result.size() < k) {
result.add(new int[]{entry.getKey(), entry.getValue()});
}
else{
if(entry.getValue() > result.peek()[1]){
result.poll();
result.add(new int[]{entry.getKey(),entry.getValue()});
}
}
}
int[] ans = new int[k];
for(int i = k - 1; i >= 0; i--) {
ans[i] = result.poll()[0];
}
return ans;
}
}