代码随想录算法训练营第十三天 | 239. 滑动窗口最大值、347.前 K 个高频元素

day_13

传送门


239 - 滑动窗口最大值

  • 思路

    • 暴力法:窗口内遍历找到最大值,要遍历很多次已经遍历过的数据
    • 单调队列法实现:维护一个最大值队列供其比较
  • 对窗口的操作

    • 先创建一个头尾都可以操作的队列,dq
    • 滑动到下一窗口的时候,上个窗口的首元素 pop,新进入滑动窗口的元素 push 进来
    • 循环上一步,直至结束
  • 单调队列的详细方法

    • 单调?增还是减?这里选择单调减(单调增的话,可能到下一窗口的时候,队列为空,无法比较了)
    • Push :每一个元素添加进来的时候,都把原先队列里小于自己的弹出去
      • 如果队列为空,直接加入进来
      • 这样就保持了队列单调减
    • Pop :遇到原先就已经pop出去的元素,不用管他们,遇到了某一窗口的最大值的时候,队列pop对头元素
    • 这样操作下来,队列的首元素就是目前窗口里的最大值了

个人理解:队列里其实可以看作只有某一窗口值,不过是把这些值设为了不可见状态,仅需维护可见状态的数字,
确实是很巧妙的想法

public class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        //  新建一个数组,存储每次窗口里的最大值
        int size = nums.length - k + 1;             //  窗口长度
        int ans[] = new int[size];                  //  答案数组
        int index = 0;                              //  答案数组的下标
        MyQueue myQueue = new MyQueue();            //  自建队列

        //  第一个窗口需要手动构造,后面的每次滑动一格
        for (int i = 0; i < k; i++) {
            myQueue.push(nums[i]);
        }

        ans[index++] = myQueue.getMax();


        //  窗口开始滑动,先取出原先出窗口的最大值,再向下滑动,i为窗口的末尾位置
        for (int i = k; i < nums.length; i++) {
            myQueue.pop(nums[i - k]);
            myQueue.push(nums[i]);
            ans[index++] = myQueue.getMax();
        }

        return ans;
    }
}

/**
 * 充当单调队列的作用
 */
class MyQueue{
    //  这里选用链表式的是由于有大量的非首尾元素移除
    Deque<Integer> deque = new LinkedList<>();

    //  pop,弹出队列头的元素
    void pop(int value){
        if (!deque.isEmpty() && deque.peek() == value)
            deque.poll();
    }

    // push,给队列尾新增元素,新增的时候pop掉前面小于他的数字
    //  新增的较大数字不应该卷走前面的大数字,保证队列元素单调递减
    void push(int value){
        while (!deque.isEmpty() && value > deque.getLast() ){
            deque.removeLast();
        }

        deque.add(value);
    }

    // getMax,取出最大值
    int getMax(){
        return deque.peek();
    }

}

347 - 前 K 个高频元素

  • 思路
    1. 找出现了几次 - 遍历数组,hash表记录次数
    2. 找前 k 个高频次 - 对出现的次数进行排序
    3. 找到后赋值输出

public class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();

        //  构建 hash 表
        for (int num : nums) {
            if (hashMap.containsKey(num)){
                var times = hashMap.get(num) + 1;
                hashMap.put(num, times);
                continue;
            }
            hashMap.put(num,1);
        }

        // 对 hash 根据 v 排序,由于 treeMap 是对 key排序的,所以选择将map移到其他地方进行排序
        //  int[k, v] 对 v排序的时候,使用 int[1]中 v 排序的
        PriorityQueue<int []> pq = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o2[1] - o1[1];
            }
        });

        //  开始将 map 逐个放入优先队列中,
        for (Map.Entry<Integer, Integer> entry : hashMap.entrySet()) {
            pq.add(new int[]{entry.getKey(), entry.getValue()});
        }

        int ans[] = new int[k];
        //  开始取队列里的队头元素,取 k 个,队列取出来的是一个 int[2]数组,取出目标的值就行
        for (int i = 0; i < k; i++) {
            ans[i] = pq.poll()[0];
        }
        
        return ans;
    }
}


发现的一些问题

  • Q239 - 滑动窗口最大值,使用 Integer 当作myQueue的参数时,只过了快30个案例

    • 但是!使用 int 就全过了。。。。这是为什么呀?
  • Q347 - 前k高频:java Comparator 不太理解

PriorityQueue<int []> pq = new PriorityQueue<>(new Comparator<int[]>() {
    @Override
    public int compare(int[] o1, int[] o2) {
        return o2[1] - o1[1];
    }
});

优先对列:根据比较规则o2[1] - o1[1],将对元素进行优先级的排序

  • 不同返回值的优先级比较,(差值是 o2和o1优先级的差值)
    • 正数,o2排前面,
    • 负数,o2排后面
    • 零 ,保持稳定性,不用排序
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值