随想录训练营Day13|239. 滑动窗口最大值,347.前 K 个高频元素
标签: LeetCode闯关记
1. 239. 滑动窗口最大值
1.1 算法实现
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
ArrayDeque<Integer> deque = new ArrayDeque<>();
int resLen = nums.length - k + 1;
int[] res = new int[resLen];
int idx = 0;
//维护deque双向队列,用于存储nums[]数组中的元素下标
// eg: head---0--1--2--3--tail
// 达到两个目的,1. 单调递减,保证deque.peek()为最大值; 2. deque中的值的范围在[i - k + 1, i]
for (int i = 0; i < nums.length; i++) {
//保证deque中的值的范围在[i - k + 1, i]
while(!deque.isEmpty() && deque.peek() < i - k + 1){
deque.pollFirst();
}
//单调递减,保证deque.peek()为最大值;so, 添加进来的元素下标所代表的元素必定要比末尾的小,否则弹出末尾元素
while (!deque.isEmpty() && nums[i] > nums[deque.peekLast()] ){
deque.pollLast();
}
deque.offer(i);//出现问题: 错写成了deque.push(i),offer() 方法将元素添加到队列的尾部,而 push() 方法将元素添加到队列的头部,也就是在队列的最前面插入元素。
//维护好deque后,当i完成对第一个滑动窗口的遍历之后,每i++,只需将deque.peek()获得,即为当前滑动窗口的maxvalue;
if(i >= k - 1){
res[idx] = nums[deque.peek()];
idx++;
}
}
return res;
}
}
1.2 题目总结
2. 347.前 K 个高频元素
2.1 算法实现
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//1.用map来统计出现的元素,和元素出现的频率<k,v>
Map<Integer,Integer> map = new HashMap<>();
for (int num :nums) {
map.put(num, map.getOrDefault(num,0)+1);
}
//2.在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
//优先队列实现小顶堆
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)-> pair1[1]- pair2[1]);
for(Map.Entry<Integer,Integer> entry: map.entrySet()){
if(pq.size() < k){ //小顶堆元素个数小于k个时直接相加
pq.add(new int[]{entry.getKey(),entry.getValue()});
}else{
if(entry.getValue() > pq.peek()[1]){
pq.poll();
pq.add(new int[]{entry.getKey(), entry.getValue()});
}
}
}
//新建一维数组来存放前k个高频元素,逆序遍历
int[] topFrequent = new int[k];
for (int i = k - 1; i >= 0; i--){//逆序遍历,先弹出的是堆的根,出现的次数少,放在新数组的最后面
topFrequent[i] = pq.poll()[0];//使用 pq.poll() 方法从优先队列中弹出一个元素(即出现频率最低的元素),然后通过 [0] 来获取该元素的值 num,并将它存储到 topFrequent 数组的下标为 i 的位置中。由于我们是从 k-1 开始逆序遍历的,因此我们可以保证存储到 topFrequent 数组中的元素是按照从高到低的顺序排列的。
}
return topFrequent;
}
}
2.2 题目总结
解题思路: 1.用map来统计出现的元素,和元素出现的频率<k,v> 2.在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数 . 优先队列实现小顶堆 3.新建一维数组来存放前k个高频元素,逆序遍历
思考点:
- 使用hashmap的目的?
- 为什么不用小顶堆?
- 怎么在PriorityQueue中用compare方法实现小顶堆?
- 为什么最后是逆序遍历?
Q:什么时候实现的comparator接口? A:在创建 PriorityQueue 对象时,可以传入一个 Comparator 对象来自定义元素之间的比较方式。在本例中,使用了 lambda 表达式实现了一个匿名的 Comparator 对象来按照元素出现的频率从小到大排序,即小顶堆。 Comparator 接口是一个函数式接口,只有一个抽象方法 compare(),用来定义对象之间的排序规则。lambda 表达式可以替代匿名内部类的方式来实现这个接口,简化了代码的书写。这个 lambda 表达式的形式是:
(pair1, pair2) -> pair2[1] - pair1[1]
它的参数是两个 int 数组 pair1 和 pair2,返回值是一个 int,表示 pair2 的第二个元素减去 pair1 的第二个元素。这样,当 PriorityQueue 对元素进行排序时,会根据这个比较结果来确定元素的优先级。因为这个 lambda 表达式的实现是对第二个元素进行比较,所以最终 PriorityQueue 中的元素会按照它们在 map 中出现的次数从大到小排序。
3.今日感悟
看着标准答案打的代码,需要再学习PriorityQueue的实现方法,很多语法都不是很懂,需要再看看. 3.28补3.27