用栈实现一个队列
使用栈实现队列的下列操作: push(x) -- 将一个元素放入队列的尾部。 pop() -- 从队列首部移除元素。 peek() -- 返回队列首部的元素。 empty() -- 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明:
你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
题解:
/**
* 初始化构造函数
*/
const MyQueue = function(){
this.stack1 = [];
this.stack2 = [];
}
/**
* @param {number} x
* @return {void}
*/
MyQueue.prototype.push = function(x){
this.stack1.push(x)
}
/**
* @return {number}
*/
MyQueue.prototype.pop = function(){
if(!this.stack2.length){
while(this.stack1.length){
this.stack2.push(this.stack1.pop())
}
}
return this.stack2.pop()
}
/**
* @return {number}
*/
MyQueue.prototype.peek = function(){
if(!this.stack2.length){
while(this.stack1.length){
this.stack2.push(this.stack1.pop());
}
}
//取栈2的栈顶元素
return this.stack2[this.stack2.length - 1];
}
/**
* @return {boolean}
*/
MyQueue.prototype.empty = function(){
return !this.stack1.length && !this.stack2.length;
}
规则:
栈:先进后出;队:先进先出。让栈底的元素首先被取出。
双端队列-滑动窗口问题
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: 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
1 [3 -1 -3] 5 3 6 7
1 3 [-1 -3 5] 3 6 7
1 3 -1 [-3 5 3] 6 7
1 3 -1 -3 [5 3 6] 7
1 3 -1 -3 5 [3 6 7]
最大值分别对应:3 3 5 5 6 7
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。
题解1:双指针 + 遍历
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
const maxSlidingWindow = function(nums, k){
const res = [];
let left = 0;
let right = k - 1;
while(right < nums.length){
//计算最大值
const max = compMax(nums, left, right);
res.push(max);
left++;
right++;
}
return res;
}
/**
* @param {number[]} nums
* @param {number} left
* @param {number} right
* @return {number}
*/
const compMax = function(nums, left, right){
if(!nums.length) return;
let maxNum = nums[left];
for(let i = left; i <= right; i++){
if(nums[i] > maxNum) maxNum = nums[i];
}
return maxNum;
}
时间复杂度O(kn)
题解2:双端队列
/**
* @param {number[]} nums
* @param {number} k
* @return {number[]}
*/
const maxSlidingWindow = function(nums, k){
const res = [];
const deque = [];
for(let i = 0; i < nums.length; i++){
//小于队尾的值入队是防止队头的值去掉后,剩下的数值中最大值丢失问题(递减)
//遇到比队尾大的值,把小的值末尾出队
//并把大的值入队保证队头的值去掉后仍是后面数中的最大值
while(deque.length && nums[deque[deque.length - 1]] < nums[i]){
deque.pop()
}
//保存索引,为了准确去掉滑动窗口已滑过的值
deque.push(i);
//小于i-k 即滑动窗口滑过的索引
while(deque.length && deque[0] <= i - k){
//队头出队
deque.shift();
}
// 窗口满才会出现最大值
if(i >= k - 1){
//放入结果集
res.push(nums[deque[0]])
}
}
return res;
}
时间复杂度O(n), 在窗口发生移动时,只根据发生变化的元素对最大值进行更新,有效的递减队列。
规则:
双端队列就是允许在队列的两端进行插入和删除的队列。既允许使用 pop、push 同时又允许使用 shift、unshift 的数组。