239. 滑动窗口最大值

public class MyQueue{  //构建自己的队列及相关方法,add,poll,peek
     Deque<Integer> deque=new LinkedList<>();
     void add(int val) {
         while(!deque.isEmpty()&&val>deque.getLast()) {//保证队列按照从大到小的顺序,
             deque.removeLast();
         }
         deque.add(val);//deque.add(val)答案是这样写得(注意栈和队列之间的区别,不能用push,应该用add)
     }
     void poll(int val) {//在弹出队列头时,比较当前要弹出的数值是否等于队列头,如果相等就弹出,因为有可能要求弹出的数组下标对应的数值之前已经被弹出了
         if(!deque.isEmpty()&&val==deque.peek()) {
             deque.poll();
         }
     }
     int peek() {
         return deque.peek();//返回队列头,队列头始终是这一个队列的最大值
     }
}
public class MaxSlidingWindow {    
     public int[] maxSlidingWindow(int[] nums,int k) {         
         if(nums.length<=0)
             return null;
         if(nums.length==1)//一定不要忘了这种情况,因为是返回一个数组类型
             return nums;
         int[] res=new int[nums.length+1-k];//因为一个窗口是k个大小,一共会有数组长度+1减k的大小
         int num=0;
         MyQueue myqueue=new MyQueue();
         for(int i=0;i<k;i++) {
             myqueue.add(nums[i]);   //先把第一个窗口加入队列
         }
         res[num++]=myqueue.peek();//不要忘记把第一个窗口的最大值加入结果数组中
         for(int i=k;i<nums.length;i++) {//逐步推进窗口前进
             myqueue.poll(nums[i-k]);//判断如何删掉窗口左端口的数(也就是删除队列头)
             myqueue.add(nums[i]);//判断如何加入窗口右端口的数(也就是添加队列尾)
             res[num++]=myqueue.peek();//每前进一步,就记录该窗口的最大值。
         }
         return res;    
     }
}

思路简述

运用了单调队列的思想方法,也就是队列是递增或者是递减的,这样利于寻找每个窗口里的最大值。

在逐步用窗口检测已给数组的时候,需要另外维护这样一个单调队列。这个队列是单调递减的,队头是最大值。
重点在于这个队列不一定要维护整个数组,而是维护窗口大小内最有可能成为最大值的就可以。也就是说,这个队列的大小就在1-k(给定的窗口大小)。
那么具体怎么维护吗?也就是在队列加入新元素进行判断,如果之前队列里的旧元素比新元素小,那么就直接删去那些旧元素,让能者上位。
因为在被检测数组的眼中,是将窗口大小的框逐步向前移动,那么主要过程就是,删除框左端的数组元素,并增加框右端后面的数据元素。然后每次都要记录一下这次框里的最大值。
删除元素,也就是将队列头的元素抛出,但是这里需要注意,因为之前队列添加新元素时发生的新旧元素的对比,原定的数组下标的元素很有可能已经被抛出了,所以需要判断一下此时队列头和数组里需要删除的元素是否一致,如果一致再删除队列头。

注意,每次移动窗口时,单调队列的判断顺序应该是:
1.判断是否移除队头
2.增加队尾
3.返回队头(也就是返回此次窗口最大值)

347. 前K个高频元素

public int[] topKFrequent(int[] nums,int k) {
    int[] result=new int[k];
    HashMap<Integer,Integer> map=new HashMap<>();
    for(int num:nums) {
        map.put(num, map.getOrDefault(num, 0)+1);
    }
    Set<Map.Entry<Integer,Integer>> entries=map.entrySet();  //Map.Entry<Integer,Integer>是一种类型,map.entrySet是返回这种类型的集合
    // 根据map的value值正序排,相当于一个小顶堆
    PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
    for (Map.Entry<Integer, Integer> entry : entries) {
        queue.offer(entry);
        if (queue.size() > k) {
            queue.poll();
        }
    }
    for (int i = k - 1; i >= 0; i--) {
        result[i] = queue.poll().getKey();
    }
    return result;
}

思路简述

这个题的关键就是维护一个长度为K的优先级队列。(要熟练应用这种思想,只记录必要个数的元素就行,不用全部维护) 解题的步骤就是记录每个数组元素的出现次数,然后按照次数对数组元素进行排序,最后返回前K个元素。
先用map来存储,key是数组元素,value是该元素出现的次数。
然后用优先级队列来存储map。也就是把map.entrySet里的元素逐个弹入队列中,队列会自动排序。然后如果队列里的元素超过了k,那么就弹出队头(这个优先级队列设置的是从小到大,这样弹出比较方便,也就是每次弹出去的队头都是出现次数最少的数组元素)
最后再把队列里存储的entryset里的key都转移到数组中就可以了。因为可以按任意顺序返回答案,所以数组里的顺序就不是很重要了。

关键点

1.维护一个长度为K的优先级队列。(要熟练应用这种思想,只记录必要个数的元素就行,不用全部维护)
1.单调队列的应用


leetcode      队列 单调队列

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!