第十三天| 第五章 栈与队列part 03 239.滑动窗口最大值 347.前 K 个高频元素
239.滑动窗口最大值(第一道困难题)
-
题目链接:https://leetcode.cn/problems/sliding-window-maximum/
-
文章讲解:https://programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
-
题目介绍:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
- 返回 滑动窗口中的最大值 。
-
解法:
- 思路:大致的思路是这样,滑动窗口就类似于一个队列,每次移动的时候,丢弃的元素就poll掉,新加入的元素就offer进来,然后我每移动一次就get一下队列的maxValue。
-
- 那我到底什么时候poll呢?怎么poll可以保证每次peek的时候就是最大值?
- 这里存在两种移除的情况:
- 第一种情况,为保证队列的出口处无时无刻都是最大值,在加入元素时,保证队列内元素单调递减。即只要前面有元素小于当前元素,就要移除。(这里判断当前元素和队列的最后一个元素即可保证上述逻辑)。
- 第二种情况,加入元素时,如果队列是满的(数量等于k),需要移除,移除的是哪一个呢?因为队列是满的,也就意味着前三个元素一直单调递减,即队列中的第一个元素是最大值。因此,当我们向后移动时,如果前三个元素中的第一个和队列出口处的元素相等,就移除这个元素。
- 所以我们每次需要先进行判断,滑动窗口的前一个元素(下标为i - k的元素)是否要从队列中移除,再向队列中加入元素,再将出口处的最大值peek出来。
- 这里存在两种移除的情况:
- 那我到底什么时候poll呢?怎么poll可以保证每次peek的时候就是最大值?
-
代码:
-
第一种:自定义单调队列
-
class Solution { public int[] maxSlidingWindow(int[] nums, int k) { MyQueue myQueue = new MyQueue(); int[] res = new int[nums.length - k + 1]; int count = 0; for (int i = 0; i < k; i++) { myQueue.add(nums[i]); } res[count++] = myQueue.peek(); for (int i = k; i < nums.length; i++) { myQueue.poll(nums[i - k]); myQueue.add(nums[i]); res[count++] = myQueue.peek(); } return res; } } 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.peek()) { deque.removeLast(); } deque.add(value); } public int peek() { return deque.peek(); } }
-
-
第二种:使用双端队列模拟单调队列
-
这里的思路比较简单,就是定义一个双端队列来存数组的下标
-
i为nums下标,是要在[i - k + 1, i] 中选到最大值,只需要保证两点
- 1.队列头结点需要在[i - k + 1, i]范围内,不符合则要弹出
- 2.既然是单调,就要保证每次放进去的数字要比末尾的都大,否则也弹出
-
最后,因为单调,当i增长到符合第一个k范围的时候,每滑动一步都将队列头节点放入结果就行了。
-
class Solution { public int[] maxSlidingWindow(int[] nums, int k) { Deque<Integer> deque = new ArrayDeque<>(); int[] res = new int[nums.length - k + 1]; int idx = 0; for (int i = 0; i < nums.length; i++) { // deque中的元素要满足[i - k + 1, i] while (!deque.isEmpty() && deque.peek() < i - k + 1) { deque.pollFirst(); } while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) { deque.pollLast(); } deque.offer(i); if (i >= k - 1) { res[idx++] = nums[deque.peek()]; } } return res; } }
-
-
347.前 K 个高频元素
-
题目链接:https://leetcode.cn/problems/top-k-frequent-elements/
-
文章讲解:https://programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
-
题目介绍:给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
-
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]
-
-
解法:
- 思路:采用优先级队列的小顶堆方式
-
代码:
-
class Solution { public int[] topKFrequent(int[] nums, int k) { Map<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]); for (Map.Entry<Integer, Integer> entry : map.entrySet()) { if (pq.size() < k) { pq.add(new int[]{entry.getKey(), entry.getValue()}); } else { if (entry.getValue() > pq.peek()[1]) { pq.poll(); pq.add(new int[]{entry.getKey(), entry.getValue()}); } } } int[] res = new int[k]; for (int i = 0; i < k; i++) { res[i] = pq.poll()[0]; } return res; } }
-