「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」
前言
每天至少一道算法题,死磕算法
今天我们再把队列深入一下,以前我们做过栈中的最小值,现在我们来做队列中的最大值
做这种栈中的最小值,队列中的最大值这种题目,你就记住如果是最小值那么辅助数组是升序排列,如果是最大值那么辅助数组是降序排列,这是解这种题最牛逼的思路。因为这个辅助数组只用栈或者队列的操作是完不成的,所以我们通常使用双端队列,也就是push,pop,unshift,shift这些操作都能使用,所以这种解题方法也称为双端队列法,我们一般定义这个数组为deque,也就是双端队列
今天的这两道题非常相似,一道是剑指 Offer 59 - II. 队列的最大值中等难度的,一道是239. 滑动窗口最大值困难(是不是超兴奋)难度的
题目
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
示例 1:
输入:
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:
输入:
["MaxQueue","pop_front","max_value"]
[[],[],[]]
输出: [null,-1,-1]
思路
第一步,从题目中提取关键字
- 1.完成一个队列,有一些基本操作,最主要的是写一个获取最大值函数
- 2.若队列为空,pop_front 和 max_value 需要返回 -1
第二步,分析
- 1.题中需要获取最大值,我们总不能用Math.max(),虽然也能实现,但是没有算法思想可言,面试是拿不出手的。所以只用一个队列是实现不了这个功能的,所以记得我们做最小栈题目的时候说的,这种一般都需要
辅助数组,用来存取最大值,那么问题来了如何往这个辅助数组里面存取数值么,什么时候push,什么时候shift(因为题目是队列,所以是shift操作)呢 - 2.辅助数组如何push?我们上面说了,最大值问题要
降序排列- 如果当前辅助数组中没有值的话,那么push
- 如果当前数组中有值
- 当要push的值比辅助数组中最后一位小,那么直接push
- 当要push的值比辅助数组中最后一位大,那么就不符合降序排列了,我们把不符合条件的数值pop出去,直到辅助数组中最后一个数大于要插入进来的值,那么就push 我们优化一下,优化成什么时候不需要push,也就是当数组中有值,并且当要push的值比辅助数组中最后一位大的时候不需要push,此时我们需要while循环,把辅助数组中比要插入的数小的数都pop出去
- 3.辅助数组什么时候shift? 如果要出队列的值和辅助数组的第一位数相等的话,那么就可以出列
题解
var MaxQueue = function() {
// 队列
this.queue = [];
// 辅助数组,双端队列,deque
this.deque = [];
};
/**
* @return {number}
*/
MaxQueue.prototype.max_value = function() {
// 如果队列中没有值的话,那么返回-1
if(!this.queue.length){
return -1;
}
// 因为我们的deque是递减的,所以只要返回第一位就可以了
return this.deque[0];
};
/**
* @param {number} value
* @return {void}
*/
MaxQueue.prototype.push_back = function(value) {
// queue正常push
this.queue.push(value);
// 当数组中有值,并且当要push的值比辅助数组中最后一位大的时候不需要push,此时我们需要while循环,把辅助数组中比要插入的数小的数都pop出去
while(this.deque.length&&value>this.deque[this.deque.length-1]){
this.deque.pop();
}
// 其他时候都需要push
this.deque.push(value);
};
/**
* @return {number}
*/
MaxQueue.prototype.pop_front = function() {
// 队列中无值返回-1
if(!this.queue.length){
return -1;
}
let value = this.queue.shift();
// 如果要出队列的值和辅助数组的第一位数相等的话,那么就可以出列
if(this.deque[0]===value){
this.deque.shift();
}
return value;
};
接下来,我们在来看一道类似的题,大家可要注意哦,这可是一道困难的题目哦
题目
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
这道题仔细看一看,品一品,
这个滑动窗口是不是就是一个队列呀;
这个获取滑动窗口中的最大值,是不是就是获取队列中的最大值呀。
那和上面的题目是不是一样呀,😝。
当然这道题里面我们可以只用一个队列,因为上面一道题里面我们不知道shift出去的值是多少,所以要存储在queue里面,这道题我们可以清楚的知道要shift的出去的值,所以我们可以只用duque就可以解出来了
题解
// 这道题就是一个模板题
var maxSlidingWindow = function(nums, k) {
let result = [];
// 只需要设置一个双端队列,因为我们知道shift出来的值是多少
let duque=[];
for(let i=0;i<nums.length;i++){
// 什么时候push
while(duque.length&&nums[i]>duque[duque.length-1]){
duque.pop();
}
duque.push(nums[i]);
// 什么时候pop
if(i>k-1&&nums[i-k]===duque[0]){
duque.shift();
}
result.push(duque[0]);
}
return result.slice(k-1);
};