239. 滑动窗口最大值 (一刷至少需要理解思路)
参考这篇OI wiki的文章我感觉写得很棒单调队列 - OI Wiki,以及这篇很生动形象的知乎文章,大佬真的写得很好算法学习笔记(66): 单调队列 - 知乎
名言如下
“如果一个选手比你小还比你强,你就可以退役了。”——单调队列的原理
主要是要找到有可能的最大值
class MyQueue{
Deque<Integer> deque=new LinkedList<>();
// 弹出元素时要比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
// 同时要判断队列当前是否为空
void poll(int val){
if(!deque.isEmpty() && val==deque.peek()){
deque.poll();
}
}
//添加元素时,如果要添加的元素大于入口处元素,就将入口元素弹出,如此往复
//直到push的数值小于等于队列入口元素的数值为止。
//保证队列元素单调递减
//比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
void add(int val){
while(!deque.isEmpty()&& val>deque.getLast()){
deque.removeLast();
}
deque.add(val);
}
// 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
int peek(){
return deque.peek();
}
}
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length ==1){
return nums;
}
int len=nums.length-k+1;
//存放结果的数组
int []res=new int[len];
int num=0;
MyQueue myQueue=new MyQueue();
for(int i =0;i<k;i++){
myQueue.add(nums[i]);
}
res[num++]=myQueue.peek();//记录前k个元素的最大值,因为是单调递减队列
for(int i=k;i<nums.length;i++){
//滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
myQueue.poll(nums[i-k]);
//滑动窗口加入最后面的元素
myQueue.add(nums[i]);
res[num++]=myQueue.peek();
}
return res;
}
}
347.前 K 个高频元素 (一刷至少需要理解思路)
主要是新学了一个priorityQueue 用差值来决定元素顺序,然后还用到比较器comparator和lambda表达式,
-
Map.Entry<Integer,Integer>
:这是Map
接口的一个内部接口,称为Entry
。它代表映射中的一个键值对。这里使用了泛型<Integer, Integer>
来指定键和值都是Integer
类型。 -
entry: map.entrySet()
:这部分调用了map
对象的entrySet()
方法。entrySet()
方法返回一个视图(Set<Map.Entry<Integer, Integer>>
),包含了映射中的所有键值对。这个视图是一个集合,其中的每个元素都是Map.Entry<Integer, Integer>
类型,代表一个键值对。 -
for(Map.Entry<Integer,Integer> entry : map.entrySet())
:这是一个增强型for循环(也称为for-each循环),用于遍历entrySet()
返回的集合。每次循环迭代,变量entry
都会被赋值为集合中的下一个Map.Entry
对象。
用来表示键值对然后key就是数字 value就是出现次数,后面思路就是先hash后面创建一个小顶堆,先弹出的就是堆的根是出现次数最少的,所以从后面插入数组
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]);
/*这部分代码初始化了一个PriorityQueue对象,这个队列将会包含int[]类型的元素。
PriorityQueue是Java中的一个泛型类,可以包含任意类型的对象,这里指定了int[]作为元素类型。
(pair1,pair2)->pair1[1]-pair2[1]:这是一个Lambda表达式,
用作比较器(Comparator),定义了队列中元素的优先级。
Lambda表达式接收两个参数(在这里分别是pair1和pair2),这两个参数都是int[]类型。
pair1[1]-pair2[1]:这个表达式计算两个数组第二个元素的差值。
优先队列使用这个差值来决定元素的顺序。如果结果是负数,表示pair1应该排在pair2之前;
如果是正数,pair2排在pair1之前;如果是零,则认为两者相等。*/
for(Map.Entry<Integer,Integer> entry:map.entrySet()){//小顶堆只需要维持k个元素有序
if(pq.size()<k){//小顶堆元素个数小于k个时直接加
pq.add(new int[]{entry.getKey(),entry.getValue()});
}else{
if(entry.getValue()>pq.peek()[1]){//当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)
pq.poll();//弹出队头(小顶堆的根结点),即把堆里出现次数最少的那个删除,留下的就是出现次数多的了
pq.add(new int[]{entry.getKey(),entry.getValue()});
}
}
}
int[] ans = new int[k];
for(int i=k-1;i>=0;i--){//依次弹出小顶堆,先弹出的是堆的根,出现次数少,后面弹出的出现次数多
ans[i] = pq.poll()[0];
}
return ans;
}
}