239. 属于hard的题目,目前能力不够哈哈,直接看了carl哥的视频讲解,大差不差的理解了。附上视频的链接在此:239. 滑动窗口最大值。(自定义单调队列)
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 1) {
return nums;
}
//res数组的长度
int len = nums.length - k + 1;
int[] res = new int[len];
//自定义的单调队列
MyQueue myQueue= new MyQueue();
int num = 0;
//先将前k个值加入到队列中
for(int i=0; i<k; i++){
myQueue.add(nums[i]);
}
//当前初始滑动窗口内的最大值,先加入到res中
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> que = new LinkedList<>();
// 只有当poll值==peek的值的时候才poll,说明这个值已经不在当前的窗口内,要去寻找下一个窗口的最大值
public void poll(int val){
if(!que.isEmpty() && que.peek() == val){
que.poll();
}
}
// 在push的时候需要进行比较,移除所有比val小的值
//添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出,保证队列元素单调递减
//比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
public void add(int val){
while(!que.isEmpty() && que.getLast() < val){
que.removeLast();
}
que.add(val);
}
//获取最大值就是队列出口的值
public int peek(){
return que.peek();
}
}
347. 这一题呢,其实不算难,但是我没做出来。主要是因为对堆和优先队列的不熟悉,这个数据结构很少用到,一时间没想起来,想起来估计也忘记怎么用了哈哈。看了题解之后感觉还是蛮简单的,只要掌握优先队列就行了,算是复习了下数据结构和算法了。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();//key为数组元素值,val为对应出现次数
for(int num:nums){
map.put(num,map.getOrDefault(num,0)+1);
}
//在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
//出现次数按从队头到队尾的顺序是从大到小排,出现次数最多的在队头(相当于大顶堆)
// pair1和pair2分别代表队列中的两个元素(即两个数组)。
// pair2[1]-pair1[1]表示比较这两个数组的第二个元素的值。
// 如果结果为正数,意味着pair2的第二个元素比pair1的第二个元素大,因此在优先队列中,pair2应该排在pair1之前。
// 如果结果为负数,意味着pair1的第二个元素比pair2的第二个元素大,pair1将排在pair2之前。
// 如果结果为零,则认为这两个元素在排序时是相等的。
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2)->pair2[1]-pair1[1]);
for(Map.Entry<Integer,Integer> entry:map.entrySet()){//大顶堆需要对所有元素进行排序
pq.add(new int[]{entry.getKey(),entry.getValue()});
}
int[] ans = new int[k];
for(int i=0;i<k;i++){//依次从队头弹出k个,就是出现频率前k高的元素
ans[i] = pq.poll()[0];
}
return ans;
}
}
总结: 上面这两道题都是说一刷了解思路,题目还是蛮有意思的,希望二刷再好好理解下吧。