队列
- 一个 先进先出 的数据结构
- JavaScript中没有队列,但可以用 Array 实现队列的所有功能
- 队列的常用操作:
push
,shift
队列的应用场景
- 需要 先进先出 的场景
- 比如:食堂排队打饭、JS 异步中的任务队列、计算最近请求次数
场景一:食堂排队打饭
- 只有一个窗口,排成队列
- 先进先出,保证有序
场景二:JS 异步中的任务队列
- JS 是单线程,无法同时处理异步中的并发任务
- 使用任务队列先后处理异步任务
setTimeout(() => {
console.log(1)
},0);
console.log(2);
// 2
// 1
场景三:计算最近请求次数
LeetCode:933. 最近的请求次数
写一个 RecentCounter
类来计算特定时间范围内最近的请求。
请你实现 RecentCounter
类:
RecentCounter()
初始化计数器,请求数为 0 。int ping(int t)
在时间t
添加一个新请求,其中t
表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在[t-3000, t]
内发生的请求数。- 保证 每次对
ping
的调用都使用比之前更大的t
值。
示例 1:
输入: ["RecentCounter", "ping", "ping", "ping", "ping"] [[], [1], [100], [3001], [3002]]
输出: [null, 1, 2, 3, 3]
解释:
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1); // requests = [1],范围是 [-2999,1],返回 1
recentCounter.ping(100); // requests = [1, 100],范围是 [-2900,100],返回 2
recentCounter.ping(3001); // requests = [1, 100, 3001],范围是 [1,3001],返回 3
recentCounter.ping(3002); // requests = [1, 100, 3001, 3002],范围是 [2,3002],返回 3
思路:
- 越早发出的请求越早不在最近3000ms内的请求里
- 满足先进先出,考虑使用队列
解题步骤:
- 有新请求就入队,3000ms前发出的请求出队
- 队列的长度就是最近请求次数
var RecentCounter = function() {
this.q = [];
};
/**
* @param {number} t
* @return {number}
*/
RecentCounter.prototype.ping = function(t) {
this.q.push(t);
while(this.q[0] < t - 3000) {
this.q.shift();
}
return this.q.length
};
/**
* Your RecentCounter object will be instantiated and called as such:
* var obj = new RecentCounter()
* var param_1 = obj.ping(t)
*/
时间复杂度:O(n)
空间复杂度:O(n)
LeetCode:225. 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack
类:
void push(int x)
将元素 x 压入栈顶。int pop()
移除并返回栈顶元素。int top()
返回栈顶元素。boolean empty()
如果栈是空的,返回true
;否则,返回false
。
注意:
- 你只能使用队列的基本操作 —— 也就是
push to back
、peek/pop from front
、size
和is empty
这些操作。 - 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入: ["MyStack", "push", "push", "top", "pop", "empty"] [[], [1], [2], [], [], []]
输出: [null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False。
解题步骤:
- 栈
pop
弹出最后一个元素,用队列shift
弹出前n-1
个元素并放回栈数组中,并返回第n
个元素
var MyStack = function() {
this.s = [];
};
/**
* @param {number} x
* @return {void}
*/
MyStack.prototype.push = function(x) {
this.s.push(x);
};
/**
* @return {number}
*/
MyStack.prototype.pop = function() {
let pop = [];
let temp = [];
while(this.s.length != 1) {
pop.push(this.s.shift());
}
temp = this.s;
this.s = pop;
pop = temp;
return pop[0]
};
/**
* @return {number}
*/
MyStack.prototype.top = function() {
return this.s[this.s.length - 1]
};
/**
* @return {boolean}
*/
MyStack.prototype.empty = function() {
while(this.s.length != 0) {
return false
}
return true
};
/**
* Your MyStack object will be instantiated and called as such:
* var obj = new MyStack()
* obj.push(x)
* var param_2 = obj.pop()
* var param_3 = obj.top()
* var param_4 = obj.empty()
*/
LeetCode:232. 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push
、pop
、peek
、empty
):
实现 MyQueue
类:
void push(int x)
将元素x
推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
说明:
- 你 只能 使用标准的栈操作 —— 也就是只有
push to top
,peek/pop from top
,size
, 和is empty
操作是合法的。 - 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
示例:
输入: ["MyQueue", "push", "push", "peek", "pop", "empty"] [[], [1], [2], [], [], []]
输出: [null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
解题思路:
- 队列
pop
弹出第一个元素,用栈pop
逆序弹出后n-1
个元素放回队列数组中,并返回第一个元素
var MyQueue = function() {
this.q = [];
};
/**
* @param {number} x
* @return {void}
*/
MyQueue.prototype.push = function(x) {
this.q.push(x);
};
/**
* @return {number}
*/
MyQueue.prototype.pop = function() {
let pop = [];
let temp = [];
while(this.q.length != 1) {
pop.push(this.q.pop());
}
temp = this.q;
this.q = pop.reverse();
pop = temp;
return pop[0]
};
/**
* @return {number}
*/
MyQueue.prototype.peek = function() {
return this.q[0]
};
/**
* @return {boolean}
*/
MyQueue.prototype.empty = function() {
return !this.q.length
};
/**
* Your MyQueue object will be instantiated and called as such:
* var obj = new MyQueue()
* obj.push(x)
* var param_2 = obj.pop()
* var param_3 = obj.peek()
* var param_4 = obj.empty()
*/
LeetCode:346. 滑动窗口的平均值
给定一个整数数据流和一个窗口大小,根据该滑动窗口的大小,计算滑动窗口里所有数字的平均值。
实现 MovingAverage
类:
MovingAverage(int size)
用窗口大小size
初始化对象。double next(int val)
成员函数next
每次调用的时候都会往滑动窗口增加一个整数,请计算并返回数据流中最后size
个值的移动平均值,即滑动窗口里所有数字的平均值。
示例:
输入: inputs = ["MovingAverage", "next", "next", "next", "next"] inputs = [[3], [1], [10], [3], [5]]
输出: [null, 1.0, 5.5, 4.66667, 6.0]
解释:
MovingAverage movingAverage = new MovingAverage(3);
movingAverage.next(1); // 返回 1.0 = 1 / 1
movingAverage.next(10); // 返回 5.5 = (1 + 10) / 2
movingAverage.next(3); // 返回 4.66667 = (1 + 10 + 3) / 3
movingAverage.next(5); // 返回 6.0 = (10 + 3 + 5) / 3
解题思路:
- 创建一个队列
- 队列长度小于
size
直接求平均值 - 队列长度大于等于
size
弹出第一个元素再求平均值 - 返回平均值
/**
* Initialize your data structure here.
* @param {number} size
*/
var MovingAverage = function(size) {
this.size = size;
this.window = [];
};
/**
* @param {number} val
* @return {number}
*/
MovingAverage.prototype.next = function(val) {
if(this.window.length >= this.size){
this.window.shift();
}
this.window.push(val);
return arg(this.window);
};
let arg = (arr) => {
let sum = 0;
for(let i = 0; i < arr.length; i++){
sum = sum + arr[i];
}
return sum/arr.length;
}
/**
* Your MovingAverage object will be instantiated and called as such:
* var obj = new MovingAverage(size)
* var param_1 = obj.next(val)
*/