day_13
传送门
-
239 - 滑动窗口最大值 - 题目链接/文章讲解/视频讲解
-
347 - 前 K 个高频元素 - 题目链接/文章讲解/视频讲解
239 - 滑动窗口最大值
-
思路
- 暴力法:窗口内遍历找到最大值,要遍历很多次已经遍历过的数据
- 单调队列法实现:维护一个最大值队列供其比较
-
对窗口的操作
- 先创建一个头尾都可以操作的队列,dq
- 滑动到下一窗口的时候,上个窗口的首元素 pop,新进入滑动窗口的元素 push 进来
- 循环上一步,直至结束
-
单调队列的详细方法
- 单调?增还是减?这里选择单调减(单调增的话,可能到下一窗口的时候,队列为空,无法比较了)
- Push :每一个元素添加进来的时候,都把原先队列里小于自己的弹出去
- 如果队列为空,直接加入进来
- 这样就保持了队列单调减
- Pop :遇到原先就已经pop出去的元素,不用管他们,遇到了某一窗口的最大值的时候,队列pop对头元素
- 这样操作下来,队列的首元素就是目前窗口里的最大值了
个人理解:队列里其实可以看作只有某一窗口值,不过是把这些值设为了不可见状态,仅需维护可见状态的数字,
确实是很巧妙的想法
public class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// 新建一个数组,存储每次窗口里的最大值
int size = nums.length - k + 1; // 窗口长度
int ans[] = new int[size]; // 答案数组
int index = 0; // 答案数组的下标
MyQueue myQueue = new MyQueue(); // 自建队列
// 第一个窗口需要手动构造,后面的每次滑动一格
for (int i = 0; i < k; i++) {
myQueue.push(nums[i]);
}
ans[index++] = myQueue.getMax();
// 窗口开始滑动,先取出原先出窗口的最大值,再向下滑动,i为窗口的末尾位置
for (int i = k; i < nums.length; i++) {
myQueue.pop(nums[i - k]);
myQueue.push(nums[i]);
ans[index++] = myQueue.getMax();
}
return ans;
}
}
/**
* 充当单调队列的作用
*/
class MyQueue{
// 这里选用链表式的是由于有大量的非首尾元素移除
Deque<Integer> deque = new LinkedList<>();
// pop,弹出队列头的元素
void pop(int value){
if (!deque.isEmpty() && deque.peek() == value)
deque.poll();
}
// push,给队列尾新增元素,新增的时候pop掉前面小于他的数字
// 新增的较大数字不应该卷走前面的大数字,保证队列元素单调递减
void push(int value){
while (!deque.isEmpty() && value > deque.getLast() ){
deque.removeLast();
}
deque.add(value);
}
// getMax,取出最大值
int getMax(){
return deque.peek();
}
}
347 - 前 K 个高频元素
- 思路
- 找出现了几次 - 遍历数组,hash表记录次数
- 找前 k 个高频次 - 对出现的次数进行排序
- 找到后赋值输出
public class Solution {
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer, Integer> hashMap = new HashMap<>();
// 构建 hash 表
for (int num : nums) {
if (hashMap.containsKey(num)){
var times = hashMap.get(num) + 1;
hashMap.put(num, times);
continue;
}
hashMap.put(num,1);
}
// 对 hash 根据 v 排序,由于 treeMap 是对 key排序的,所以选择将map移到其他地方进行排序
// int[k, v] 对 v排序的时候,使用 int[1]中 v 排序的
PriorityQueue<int []> pq = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o2[1] - o1[1];
}
});
// 开始将 map 逐个放入优先队列中,
for (Map.Entry<Integer, Integer> entry : hashMap.entrySet()) {
pq.add(new int[]{entry.getKey(), entry.getValue()});
}
int ans[] = new int[k];
// 开始取队列里的队头元素,取 k 个,队列取出来的是一个 int[2]数组,取出目标的值就行
for (int i = 0; i < k; i++) {
ans[i] = pq.poll()[0];
}
return ans;
}
}
发现的一些问题
-
Q239 - 滑动窗口最大值,使用 Integer 当作myQueue的参数时,只过了快30个案例
- 但是!使用 int 就全过了。。。。这是为什么呀?
-
Q347 - 前k高频:java Comparator 不太理解
PriorityQueue<int []> pq = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o2[1] - o1[1];
}
});
优先对列:根据比较规则o2[1] - o1[1],将对元素进行优先级的排序
- 不同返回值的优先级比较,(差值是 o2和o1优先级的差值)
- 正数,o2排前面,
- 负数,o2排后面
- 零 ,保持稳定性,不用排序