day21 239. 滑动窗口最大值 347. 前 K 个高频元素

文档讲解:代码随想录(代码随想录

完成状态:  true

 239. 滑动窗口最大值

最开始只能想到暴力解法(但是由于时间复杂度的原因提交失败):

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int[] result = new int[nums.length - k + 1];
        for(int i = 0; i < nums.length - k + 1; i++){
            int temp = Integer.MIN_VALUE;
            for(int j = i; j < i + k; j++){
                if(nums[j] > temp){
                    temp = nums[j];
                }
                result[i] = temp;
            }
        }
        return result;
    }
}

        优先级队列为什么不行?因为该队列在pop出元素后,可能pop出参与下一个滑动窗口比较的值,所以它会影响元素的顺序。其不能按照滑动窗口的需求去按序pop。

        假如有一个队列,可以向里面添加和删除元素,并且可以返回当前队列的最大值。那么这道题我们就可以使用这个队列,固定其长度,每次向队尾里面添加一个元素,同时删除队头的元素,此时返回当前队列的最大值即可。但此处的和上述的优先级队列不一样的地方是,其每次pop元素都是不会影响元素的原顺序的,这也是难点,需要我们考虑要如何实现的点。

        故而需要我们自己定义一个单调队列,可以满足我们的需要,维护队列里面的元素单调递减。代码中重点代码体现在push()处,重点理解。

class MyQueue{
    private Deque<Integer> deque = new LinkedList<>();
    // 弹出元素
    public void pop(int value){
        if(!deque.isEmpty() && value == deque.peek()){
            deque.poll();
        }
    }
    // 添加元素时,如果添加的元素大于队列前面的元素,则将比自己小的元素都弹出
    // 保证队列元素单调递减
    public void push(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) {
        int[] result = new int[nums.length - k + 1];
        int j = 0;
        MyQueue myQueue = new MyQueue();
        for(int i = 0; i < k; i++){
            myQueue.push(nums[i]);
        }
        result[j++] = myQueue.peek();
        for(int i = 1; i < nums.length - k + 1; i++){
            myQueue.pop(nums[i - 1]);
            myQueue.push(nums[i + k - 1]);
            result[j++] = myQueue.peek();
        }
        return result;
    }
}

347. 前 K 个高频元素

前置知识:

  • 优先级队列:优先队列也是一种队列,不同的是,优先队列的出队顺序是按照优先级来的。对于优先队列,元素进入队列的顺序可能与其被操作的顺序不同,作业调度是优先队列的一个应用实例,它根据优先级的高低而不是先到先服务的方式来进行调度。
函数名功能介绍
boolean offer(E e)插入元素 e,插入成功返回 true,如果 e 对象为空,抛出 NullPointerException 异常,时间复杂度为 O(log2N) ,注意:空间不够时会自动扩容
E peek()获取优先级最高的元素,如果优先级队列为空,返回 null
E poll()移除优先级最高的元素并返回,如果优先级队列为空,返回 null
int size()获取有效元素的个数
void clean()清空
boolean isEmpty()检测优先级队列是否为空,空返回 true

        在Java中也实现了自己的优先队列java.util.PriorityQueue,Java中内置的为最小堆,底层维护了一个Object类型的数组。如果想要把最小堆变成最大堆可以给PriorityQueue传入自己的比较器。

  • 堆:一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。如果是大顶堆,那么其根节点一定是最大的(记住大根堆构建完并不代表是有序的)。

        大根堆:适合求前k个最小值

        小根堆:适合求前k个最大值

  • getOrDefault() 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。(hashmap.getOrDefault(Object key, V defaultValue))

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int num: nums){
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        // 使用优先级队列对频率的值进行排序
        PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]); // 按照value进行排序
        Set<Map.Entry<Integer, Integer>> entrySet = map.entrySet();
        for(Map.Entry<Integer, Integer> entry: entrySet){
            pq.add(new int[]{entry.getKey(), entry.getValue()});
            // 如果小根堆超过了k个元素
            if(pq.size() > k){
                pq.poll();
            }
        }
        int[] res = new int[k];
        for(int i = k - 1; i >= 0; i--){
            res[i] = pq.poll()[0];
        }
        return res;
    }
}

 215. 数组中的第K个最大元素

        有上题的铺垫,这道就很容易了。但此处并不是最优解法。

class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>((a,b)->b-a); // 大根堆
        for(int num: nums){
            pq.add(num);
        }
        int result = -1;
        for(int i = 0; i < k; i++){
            if(i == k -1){
                result = pq.poll();
                break;
            }
            pq.poll();
        }
        return result;
    }
}

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值