算法训练第十三天 | LeetCode 239、347

LeetCode 239 滑动窗口最大值

题目简析:整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位,返回 滑动窗口中的最大值。

思路简析:摘录了代码随想录里核心的部分

队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队里里的元素数值是由大到小的。

设计单调队列的时候,pop,和push操作要保持如下规则:

  1. pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  2. push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止

保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。

 其余的内容在代码注释里我认为是足够有助于我日后回顾的了,也是理解了一遍卡哥的思路后自己动手写了出来,有想看的朋友也不要嫌弃我注释很长hhhhh。

    class MyDeque {
        Deque<Integer> deque = new LinkedList<>();
        //编写弹出
        void poll(int val){
            //前一个:空则无数值可以弹出
            //后一个:因为是先把前k个填入了窗口进行比较,所以直到第k个,都不进行弹出
            if(!deque.isEmpty() && val == deque.peek()){
                deque.poll();
            }
        }
        //编写入队
        void add(int val){
            //发现新填入的数值大于末尾元素,就把末尾元素给弹出再加入,否则正常加入
            while(!deque.isEmpty() && val > deque.getLast()){
                deque.removeLast();
            }
            deque.add(val);
        }

        //返回最大值----代表最大值的队列前端
        int peek(){
            return deque.peek();
        }
    }
    public int[] maxSlidingWindow(int[] nums, int k) {
        //限制一下
        if(nums.length == 1){
            return nums;
        }
        MyDeque myDeque = new MyDeque();
        //无需创建length个,最后窗口会排出k-1个值
        int len = nums.length;
        int []arr = new int[len - k + 1];
        //滑动窗口最大值下标
        int count = 0;
        //为保证窗口有值,先将前k个放入滑动窗口
        for (int i = 0; i < k; i++) {
            myDeque.add(nums[i]);
        }
        //此时已经产生了一个窗口最大值,直接放入
        arr[count++] = myDeque.peek();
        for (int i = k; i < len; i++) {
            //先将窗口进行移动
            myDeque.poll(nums[i-k]);
            //因为传的是i-k,而不断传值后若没遍历到最大值的位置
            //说明上一个最大值还存在在新的窗口内
            //eg
            //窗口: [ 1 -1 3 ] -2 5
            //先将前k个入队
            //队列情况应该是:1 -1  ----add中的---removeLast--> 1 3
            //第二轮窗口: 1 [ -1 3 -2] 5
            //num[i-k] = 1
            //但是已经不在队列里,经过poll的判断 (val==1) == (deque.peek() == 1)
            //因此排出窗口的1      队列经过add后剩:   3  -2
            //下一轮也是一样的
            //第三轮窗口: 1 -1 [ 3 -2 5]
            //队列情况:① 3 -2    --add中的removeLast--->   ②  3(5>3继续removeLast)  ③经过add后剩  5
            //num[i-k] = -1 也仍然不在,排出窗口的-1
            //直到num[i-k] == 3   此时说明窗口已经划过3,因此可以把3给排出去了

            //不断滑动窗口,填入新数
            myDeque.add(nums[i]);
            //加入后已经产生最大值
            arr[count++] = myDeque.peek();
        }
        return arr;
    }

LeetCode 347前K个高频元素

    public int[] topKFrequent2(int[] nums, int k) {
        Map<Integer,Integer> map = new HashMap<>();//key为数组元素值,val为对应出现次数
        for(int num:nums){
            map.put(num,map.getOrDefault(num,0)+1);
        }
        //在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
        //出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(相当于小顶堆)
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(pair -> pair[1]));
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){
            //小顶堆只需要维持k个元素有序
            if(pq.size()<k){//小顶堆元素个数小于k个时直接加
                pq.add(new int[]{entry.getKey(),entry.getValue()});
            }else{
                if(entry.getValue()>pq.peek()[1]){
                    //当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)
                    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;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值