239. 滑动窗口最大值 347.前 K 个高频元素
239. 滑动窗口最大值 题目链接
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 sum = 0;
MyQueue q = new MyQueue();
for(int i = 0 ; i < k ; i++){
q.add(nums[i]);
}
res[sum++] = q.peek();
for(int i = k ; i < nums.length;i++){
//滑动窗口移除最前面的元素,移除是判断该元素是否放入队列
q.poll(nums[i - k]);
//滑动窗口加入最后面的元素
q.add(nums[i]);
//记录对应的最大值
res[sum++] = q.peek();
}
return res;
}
}
//构造单调队列
class MyQueue{
Deque<Integer> q = new LinkedList<>();
//弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
//同时判断队列当前是否为空
void poll(int val){
if(!q.isEmpty() && val == q.peek()){
q.poll();
}
}
//添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
//保证队列元素单调递减
//比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
void add(int val){
while(!q.isEmpty() && val > q.getLast()){
q.removeLast();
}
q.add(val);
}
//队列队顶元素始终为最大值
int peek(){
return q.peek();
}
}
设计单调队列的时候,pop,和push操作要保持如下规则:
- pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
- push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。
347.前 K 个高频元素题目链接
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
//存储每个元素出现的次数,元素为key 次数为value
for(int i = 0 ; i < nums.length;i++){
if(map.containsKey(nums[i])){
map.replace(nums[i],map.get(nums[i]) + 1);
}else{
map.put(nums[i],1);
}
}
//最小堆保存频率最大的k元素
PriorityQueue<Integer> pq = new PriorityQueue<>((a,b) -> 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[] ans = new int[k];
for(int i = k - 1; i >= 0 ; i--){
ans[i] = pq.remove();
}
return ans;
}
}
- 借助 哈希表 来建立数字和其出现次数的映射,遍历一遍数组统计元素的频率维护一个元素数目为 kkk 的最小堆
- 每次都将新的元素与堆顶元素(堆中频率最小的元素)进行比较
- 如果新的元素的频率比堆顶端的元素大,则弹出堆顶端的元素,将新的元素添加进堆中
- 最终,堆中的 kkk 个元素即为前 kkk 个高频元素