第五章 栈与队列part03
239. 滑动窗口最大值 (一刷至少需要理解思路)
题目链接/文章讲解/视频讲解: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
思路:
步骤:
-
首先,判断输入数组
nums
是否为空或长度为 0,如果是,则返回一个空数组。 -
创建一个结果数组
result
,长度为n - k + 1
,其中n
是nums
数组的长度,用来存储滑动窗口中的最大值。 -
创建一个双端队列
deque
,用来存储数组元素的索引。队列中的元素按照从大到小的顺序排列,保证队首元素始终是滑动窗口中的最大值的索引。 -
遍历数组
nums
,对于每个元素nums[i]
:- 移除超出滑动窗口范围的元素,即队首元素不在当前滑动窗口的范围内时,将其从队列中移除。
- 移除比当前元素小的元素,即将队列中比当前元素小的元素都从队列尾部移除,以保持队列的递减顺序。
- 将当前元素的索引加入队列的尾部。
- 如果已经形成了一个完整的滑动窗口(即
i >= k - 1
),则记录当前滑动窗口的最大值,即队首元素,并将其存入结果数组result
中。
-
返回结果数组
result
。
完整代码
import java.util.ArrayDeque;
import java.util.Arrays;
public class SlidingWindowMaximum {
public static int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0) {
return new int[0];
}
int n = nums.length;
int[] result = new int[n - k + 1];
int index = 0;
ArrayDeque<Integer> deque = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
// 移除超出滑动窗口范围的元素
while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
deque.pollFirst();
}
// 移除比当前元素小的元素
while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
deque.pollLast();
}
// 将当前元素加入双端队列
deque.offerLast(i);
// 记录滑动窗口的最大值
if (i >= k - 1) {
result[index++] = nums[deque.peekFirst()];
}
}
return result;
}
public static void main(String[] args) {
int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
int k = 3;
int[] result = maxSlidingWindow(nums, k);
System.out.println(Arrays.toString(result));
}
}
347.前 K 个高频元素 (一刷至少需要理解思路)
大/小顶堆的应用, 在C++中就是优先级队列
本题是 大数据中取前k值 的经典思路,了解想法之后,不算难。
题目链接/文章讲解/视频讲解:https://programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html
思路:
步骤:
- 首先,判断输入数组
nums
是否为空或长度为 0,以及k
是否小于等于 0,如果是,则返回一个空数组。 - 创建一个哈希表
freqMap
,用来统计每个元素的出现频率。 - 遍历数组
nums
,对于每个元素num
,将其加入到哈希表freqMap
中,并更新其出现频率。 - 创建一个小顶堆
heap
,并定义比较器,使得堆中的元素按照频率从小到大排序。初始时堆的大小为 0。 - 遍历哈希表
freqMap
,对于每个元素num
,将其加入到堆heap
中。- 如果堆的大小超过了
k
,则移除堆顶元素(出现频率最低的元素)。
- 如果堆的大小超过了
- 构造结果数组
result
,长度为k
。从堆heap
中依次取出元素,并存入结果数组中,注意顺序是逆序的。 - 将结果数组中的元素进行反转,使其顺序变为频率从高到低。
- 返回结果数组
result
。
完整代码
import java.util.*;
public class TopKFrequentElements {
public static int[] topKFrequent(int[] nums, int k) {
if (nums == null || nums.length == 0 || k <= 0) {
return new int[0];
}
// 统计每个元素出现的频率
Map<Integer, Integer> freqMap = new HashMap<>();
for (int num : nums) {
freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
}
// 创建小顶堆,根据元素的频率进行排序
PriorityQueue<Integer> heap = new PriorityQueue<>((a, b) -> freqMap.get(a) - freqMap.get(b));
// 遍历频率表,将频率高的元素加入堆中
for (int num : freqMap.keySet()) {
heap.offer(num);
if (heap.size() > k) {
heap.poll();
}
}
// 构造结果数组
int[] result = new int[k];
int index = 0;
while (!heap.isEmpty()) {
result[index++] = heap.poll();
}
// 结果数组中的元素顺序是逆序的,需要反转一下
for (int i = 0; i < k / 2; i++) {
int temp = result[i];
result[i] = result[k - i - 1];
result[k - i - 1] = temp;
}
return result;
}
public static void main(String[] args) {
int[] nums = {1, 1, 1, 2, 2, 3};
int k = 2;
int[] result = topKFrequent(nums, k);
System.out.println(Arrays.toString(result));
}
}
总结
https://programmercarl.com/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97%E6%80%BB%E7%BB%93.html