题目:
- 滑动窗口最大值,347.前 K 个高频元素
学习内容:
239. 滑动窗口最大值
这道题比较难。核心就是自己定义一个单调栈的规则,从队列的头部弹出并返回元素的poll();添加元素的add();从队列的队顶取元素(最大值)的peek();
- 其中poll()弹出元素时,需要比较当前要弹出的数值是否等于队列出口的数值,如果相等才弹出(因为不相等代表之前的已经被卷走了)。
- 其中add()添加元素时,首先要看添加的元素是否大于入口的元素,如果大于就卷走。
- 其中peek()获取队顶元素,不变。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int len = nums.length - k + 1; // 定义结果的长度
int[] res = new int[len];
int num = 0;
MyQueue myqueue = new MyQueue();
// 将前k个元素先add进队列
for (int i = 0; i < k; i++) {
myqueue.add(nums[i]);
}
res[num++] = myqueue.peek(); //得到第一个结果
// 窗口开始滑动
for (int i = k; i < nums.length; i++) {
myqueue.poll(nums[i - k]); // 先弹出元素
myqueue.add(nums[i]); // 再压入元素
res[num++] = myqueue.peek(); // 再得到最大值的结果
}
return res;
}
}
class MyQueue {
Deque<Integer> deque = new LinkedList<>();
public void poll(int val) {
if (!deque.isEmpty() && val == deque.peek()) {
deque.poll();
}
}
public void add(int val) {
while (!deque.isEmpty() && val > deque.getLast()) {
deque.removeLast();
}
deque.add(val);
}
public int peek() {
return deque.peek();
}
}
时间复杂度:O(n)
空间复杂度:O(k)
347.前 K 个高频元素
这道题使用小根堆,因为要统计前k个高频元素,频率最小的元素在小根堆的最上面,所以每次将最小的元素弹出,最后积累的是前k个高频元素。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
// 使用hashmap统计每个元素出现的次数,key为元素,value为值
HashMap<Integer, Integer> map = new HashMap();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
// 使用优先级队列,用小根堆保存频率最大的k个元素
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return map.get(a) - map.get(b);
}
});
for (Integer key : map.keySet()) {
if (pq.size() < k) {
pq.add(key);
} else if (map.get(key) > map.get(pq.peek())) {
pq.remove();
pq.add(key);
}
}
// 取出小根堆的元素
int[] res = new int[k];
int num = 0;
while (!pq.isEmpty()) {
res[num++] = pq.remove();
}
return res;
}
}
时间复杂度:O(nlogk)
空间复杂度:O(n)
总结
在队列中,我们维护元素单调递减的队列就叫做单调队列,即单调递减或单调递增的队列。同时,我们可以使用优先级队列来代替大小根堆。
学习时间:
2024.3.18