刷题日记11
今天的主题是队列——单调栈——优先级队列!
自定义单调栈
这里我们用Deque作为容器来实现单调栈,关键是要实现队列的push()方法和poll()方法。
push()方法需要让加入的值大于队列最后一个元素,如果最后一个元素小于加入值,则弹出最后一个元素。这样就能维护单调递减了。
poll()方法需要弹出正确的值,这是因为我们的单调队列不是保存了所有值的队列,里面的元素有可能已经在执行push()时因为小于添加值而被弹出了。注意单调队列是因题而异的,因为这道题是滑动窗口,我们需要弹出窗口a,[b,c]前的元素,也就是a。如果单调队列的最大值也就是第一个元素等于我们想弹出的值,也就是a,则弹出。
优先级队列
什么是优先级队列呢?
其实就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。
堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。
239. 滑动窗口最大值
很显然这道题不能用暴力方法求解,因为暴力解法复杂度为O(n*k)会超时。
这就需要用到单调栈了,单调栈指的是单调递增或者单调递减的队列。我们可以自己写一个单调栈,让它保持单调递减,这样队列最前面的值就是最大值。
class MyQueue {
private Deque<Integer> queue;
public MyQueue(){
queue = new LinkedList<>();
}
public void poll(int n){
if(!queue.isEmpty() && queue.peekFirst() == n){
queue.pollFirst();
}
}
public void push(int n){
while(!queue.isEmpty() && queue.peekLast() < n){
queue.pollLast();
}
queue.offerLast(n);
}
public int peek(){
return queue.peekFirst();
}
}
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
List<Integer> res = new LinkedList<>();
MyQueue q = new MyQueue();
// 先将前k的元素放进队列
for(int i = 0; i < k; i++){
q.push(nums[i]);
}
res.add(q.peek());
// 从k开始遍历,此时滑动窗口为[i-k+1, k]
for(int i = k; i < nums.length; i++){
// 弹出nums[i-k]元素
q.poll(nums[i-k]);
q.push(nums[i]);
res.add(q.peek());
}
return res.stream().mapToInt(x -> x).toArray();
}
}
347. 前 K 个高频元素
这道题使用的是优先级队列(堆),以后遇到类似的题目,前n个最大值,第n个最大值,这样的题目都可以用优先级队列。
那么为什么要使用小顶堆呢?这是因为大顶堆需要对所有元素(的频率)排序,而小顶堆只需要排序k个元素,小顶堆的时间复杂度更优。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
PriorityQueue<Integer> pq = new PriorityQueue<>((m1, m2) -> map.get(m1) - map.get(m2));
for(int i = 0; i < nums.length; i++){
map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
}
//在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
//出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(相当于小顶堆)
for(Map.Entry<Integer, Integer> entry : map.entrySet()){
if(pq.size() < k){
pq.offer(entry.getKey());
}else if(entry.getValue() > map.get(pq.peek())){
pq.poll();
pq.offer(entry.getKey());
}
}
int[] res = new int[k];
for(int i = 0; i < k; i++){
res[i] = pq.poll();
}
return res;
}
}