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

#LeetCode239. Sliding Window Maximum

#LeetCode 239. 视频讲解:单调队列正式登场!| LeetCode:239. 滑动窗口最大值_哔哩哔哩_bilibili

单调队列是一种单调递增或者单调递减的队列。在Java 中是通过Deque 接口来实现的,其中部分常用函数有:addFirst(E e), removeFirst(), getFirst(), addLast(E e), removeLast(), getLast() 。

之所以选择单调队列,是因为可以两端添加(称呼为:入口、出口)或者删除元素以及排序,在本题中选择的单调队列是单调递减的队列。

在poll 方法中,需要首先判断队列非空,如果当前值与队列中出口最前端元素相等,则弹出当前元素,此时意味着窗口向后移动了,最左边元素会被舍弃,在最右端加入新的元素。如果不相等,则代表原本最左边的元素已经在添加新元素(add 方法)时被弹出,此时不会弹出最前端元素,因为最前端元素本来就应该属于当前的窗口。

在add 方法中,依然需要判断队列非空,只有非空情况下才存在First 元素和Last 元素。添加元素时是比较最后一个元素与添加的元素之间的关系,如果添加元素大于入口元素数值,则入口元素弹出,直到添加元素小于等于入口元素为止,目的是保证是单调队列。

在主函数的for 循环中,首先调用的是poll 函数,目的是先检查窗口移动时最左侧元素是否已经被弹出。

滑动窗口方法代码:

class MyQueue {
    Deque<Integer> deque = new LinkedList<>();
    public void poll(int value) {
        if (!deque.isEmpty() && value == deque.peek()) {
            deque.poll();
        }
    }
    public void add(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) {
        if (nums.length == 1) {
            return nums;
        }
        int[] result = new int[nums.length - k + 1];
        int number = 0;
        MyQueue queue = new MyQueue();
        for (int i = 0; i < k; i++) {
            queue.add(nums[i]);
        }
        result[number++] = queue.peek();
        for (int i = k; i < nums.length; i++) {
            queue.poll(nums[i - k]);
            queue.add(nums[i]);
            result[number++] = queue.peek();
        }
        return result;
    }
}

#LeetCode347. Top K Frequent Elements

#LeetCode 347. 视频讲解:优先级队列正式登场!大顶堆、小顶堆该怎么用?| LeetCode:347.前 K 个高频元素_哔哩哔哩_bilibili

当看到统计元素的出现概率时,需要想到map 映射是否可以解决,key 表示元素,value 来记录次数,刚好回顾之前复习过的哈希映射。

在这里用到了优先级队列,是在队头取元素,在队尾添加元素。比较器Comparator 是优先级队列中重要接口,优先级队列不是按照插入顺序排列元素,而是按照元素的自然顺序或者根据构造时提供的Comparator 决定的优先级顺序来排列元素。

在统计了出现次数后,选择使用min-heap ,min-heap 是二叉树的一种形式,根节点是小元素,子节点均大于根节点,在队列中根节点始终是优先级最高的元素。如果使用max-heap 形式,在每次移动更新的时候,都把最大的元素弹出去了。

Map.Entry 是内部的一个接口,用于表达Map 的键值对,分别通过getKey() 和getValue() 来访问。pair1[1] - pair2[1] 是Lambda 表达式,代表的是比较的内容是Map 键值对的value 部分,也就是出现的次数。

min-heap 代码: 

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> frequentMap = new HashMap<>();
        for(int num: nums) {
            frequentMap.put(num, frequentMap.getOrDefault(num, 0) + 1);
        }
        Comparator<int[]> arrayComparator = (pair1, pair2) -> pair1[1] - pair2[1];
        PriorityQueue<int[]> result = new PriorityQueue<>(arrayComparator);
        for (Map.Entry<Integer, Integer> entry: frequentMap.entrySet()) {
            if (result.size() < k) {
                result.add(new int[]{entry.getKey(), entry.getValue()});
            }
            else{
                if(entry.getValue() > result.peek()[1]){
                    result.poll();
                    result.add(new int[]{entry.getKey(),entry.getValue()});
                }
            }
        }
        int[] ans = new int[k];
        for(int i = k - 1; i >= 0; i--) {
            ans[i] = result.poll()[0];
        }
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值