往日任务
● day 1 任务以及具体安排:https://docs.qq.com/doc/DUG9UR2ZUc3BjRUdY
● day 2 任务以及具体安排:https://docs.qq.com/doc/DUGRwWXNOVEpyaVpG
● day 3 任务以及具体安排:https://docs.qq.com/doc/DUGdqYWNYeGhlaVR6
● day 4 任务以及具体安排:https://docs.qq.com/doc/DUFNjYUxYRHRVWklp
● day 5 休息
● day 6 任务以及具体安排:https://docs.qq.com/doc/DUEtFSGdreWRuR2p4
● day 7 任务以及具体安排:https://docs.qq.com/doc/DUElCb1NyTVpXa0Jj
● day 8 任务以及具体安排:https://docs.qq.com/doc/DUGdsY2JFaFhDRVZH
● day 9 任务以及具体安排:https://docs.qq.com/doc/DUHVXSnZNaXpVUHN4
● day 10 任务以及具体安排:https://docs.qq.com/doc/DUElqeHh3cndDbW1Q
●day 11 任务以及具体安排:https://docs.qq.com/doc/DUHh6UE5hUUZOZUd0
●day 12 休息
第五章 栈与队列part03
今日内容:
● 239. 滑动窗口最大值
● 347.前 K 个高频元素
● 总结
详细布置
239. 滑动窗口最大值 (一刷至少需要理解思路)
之前讲的都是栈的应用,这次该是队列的应用了。
本题算比较有难度的,需要自己去构造单调队列,建议先看视频来理解。
题目链接/文章讲解/视频讲解:https://programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html
347.前 K 个高频元素 (一刷至少需要理解思路)
大/小顶堆的应用, 在C++中就是优先级队列
本题是 大数据中取前k值 的经典思路,了解想法之后,不算难。
题目链接/文章讲解/视频讲解:https://programmercarl.com/0347.%E5%89%8DK%E4%B8%AA%E9%AB%98%E9%A2%91%E5%85%83%E7%B4%A0.html
总结
栈与队列做一个总结吧,加油
https://programmercarl.com/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97%E6%80%BB%E7%BB%93.html
day13
滑动窗口最大值
单调队列
用deque实现,deque有加入推出看队列顶add(),poll(),peek(),队列顶也就是出口
还有getLast()和removeLast()移除队尾,队尾也就是入口
单调队列是一种思想,具体实现的模型要自己具体问题具体分析
//答案
//自定义数组
class MyQueue {
Deque<Integer> deque = new LinkedList<>();
//弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
//同时判断队列当前是否为空
void poll(int val) {
if (!deque.isEmpty() && val == deque.peek()) {
deque.poll();
}
}
//添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
//保证队列元素单调递减
//比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
void add(int val) {
while (!deque.isEmpty() && val > deque.getLast()) {
deque.removeLast();
}
deque.add(val);
}
//队列队顶元素始终为最大值
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();
//先将前k的元素放入队列
for (int i = 0; i < k; i++) {
myQueue.add(nums[i]);
}
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 Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int len = nums.length - k + 1;
int[] res = new int[len];
MyQueue q = new MyQueue();
for(int i = 0; i<k; i++){
q.add(nums[i]);
}
int index = 0;
res[index++] = q.peek();//运行完就要index++
for(int i = k; i < nums.length; i++){
q.add(nums[i]);
q.poll(nums[i - k]);//队列里应该被扔出去的那个元素,如果就在q的出口处,就把他扔出去
res[index++] = q.peek();
}
return res;
}
}
class MyQueue{
Deque<Integer> deque = new LinkedList();//LinkedList去实现deque
public void poll(int val){
//出口位置元素和val一样就推出
if(!deque.isEmpty() && deque.peek() == val){
deque.poll();
}
}
public void add(int val){
while(!deque.isEmpty() && val > deque.getLast() ){
deque.removeLast();
}
deque.add(val);
}
public int peek(){
return deque.peek();
}
}
前k个高频元素
优先级队列
pq实现小顶堆,维护k个高频元素;小顶堆每次弹出堆顶,也就是最小的元素
时间复杂度nlogk,n是因为数组遍历,logk是因为小顶堆二叉树维护k个元素logk
pq方法有加入推出offer(),poll()
//答案:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
// 优先级队列,为了避免复杂 api 操作,pq 存储数组
// lambda 表达式设置优先级队列从大到小存储 o1 - o2 为从小到大,o2 - o1 反之
PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);
//pq优先级队列,里面需要储存数字和频率,所以考虑数组或者键值对var都可以
//pq构建的时候传入比较器,o1是数组,o1[1]里面放频率,根据频率排序,返回值为负数则第一个元素放在前面
//o1[1] - o2[1],返回负数说明o1小,o1放前面,也就是从小到大排序
int[] res = new int[k]; // 答案数组为 k 个元素
Map<Integer, Integer> map = new HashMap<>(); // 记录元素出现次数
for (int num : nums) map.put(num, map.getOrDefault(num, 0) + 1);
for (var x : map.entrySet()) { // entrySet 获取 k-v Set 集合
// 将 kv 转化成数组
int[] tmp = new int[2];
tmp[0] = x.getKey();
tmp[1] = x.getValue();
pq.offer(tmp);//offer就是加进去
// 下面的代码是根据小根堆实现的,我只保留优先队列的最后的k个,只要超出了k我就将最小的弹出,剩余的k个就是答案
if(pq.size() > k) {
pq.poll();
}
}
for (int i = 0; i < k; i++) {
res[i] = pq.poll()[0]; // 获取优先队列里的元素
}
return res;
}
}
//自己写
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//1.先统计频率
//2.用从小到大的优先级队列维护k个高频
Map<Integer,Integer> map = new HashMap<>();//HashMap实现map
for(int num : nums){
map.put(num,map.getOrDefault(num, 0) + 1);
}
PriorityQueue<int[]> pq = new PriorityQueue<>((o1,o2) -> o1[1] - o2[1]);
//队列头到队列尾从小到大
for( var entry : map.entrySet()){
//遍历键值对
int[] temp = new int[2];
temp[0] = entry.getKey();
temp[1] = entry.getValue();
pq.offer(temp);
if( pq.size() > k){
pq.poll();
}
}
int[] res = new int[k];
//从后往前遍历res,不断出队即可,没有遍历队列这回事儿
for( int i = k - 1; i >= 0;i--){
res[i] = pq.poll()[0];
}
return res;
}
}
总结:代码随想录
感谢大佬分享: