队列实现
- 先进先出的数据结构,可以想象排队,先到窗口的先办理业务
- 往后添加新元素的一端称为队尾,另一端称为队首
- js没有队列,我们可以使用 Array 模拟队列所有功能
const queue = [];
// 入队
queue.push(1);
queue.push(2);
// 出队
const first = queue.shift();
const end = queue.shift();
手写模拟队列(基于对象):
class Queue {
constructor() {
this.queue = {};
this.count = 0;
// 用于记录队首的键
this.head = 0;
}
// 入队方法
enQueue(item) {
this.queue[this.count++] = item;
}
// 出队方法
deQueue() {
if (this.isEmpty()) {
return;
}
const headData = this.queue[this.head];
delete this.queue[this.head];
this.head++;
this.count--;
return headData;
}
isEmpty() {
return this.count === 0;
}
clear() {
this.queue = {};
this.count = 0;
this.head = 0;
}
}
const q = new Queue();
使用两个栈模拟队列:
// 时间复杂度:add:O(1) delete:O(n)
// 空间复杂度,整体是O(n)
class MyQueue {
stack1 = [];
stack2 = [];
add(n) {
this.stack1.push(n);
}
delete() {
const stack1 = this.stack1;
const stack2 = this.stack2;
// 将 stack1 所有元素 pop 出来,push 到 stack2
while (stack1.length) {
const n1 = stack1.pop();
if (n1 != null) {
stack2.push(n1);
}
}
// 将 stack2 执行一次 pop
const resultDelete = stack2.pop();
// 再将 stack2 所有元素 pop 出来,push 进 stack1
while (stack2.length) {
const n1 = stack2.pop();
if (n1 != null) {
stack1.push(n1);
}
}
return resultDelete || null;
}
get length() {
return this.stack1.length;
}
}
const q = new MyQueue();
q.add("a");
q.add("b");
q.add("c");
console.log(q.delete()); // a
console.log(q.length); // 2
什么场景用队列
- 需要先进先出的场景
- 比如:食堂排队打饭、JS异步中的任务队列、计算最近请求次数
1、食堂排队打饭
-
食堂只留一个窗口,学生排队打饭似春运
-
先进先出,保证有序
2、JS异步的任务队列
-
JS是单线程,无法同时处理异步中的并发任务
-
使用任务队列先后处理异步任务
3、LeetCode: 933.最近的请求次数
- 有新请求就入队,3000ms前发出的请求出队
- 队列的长度就是最近请求次数
解题思路
- 越早发出的请求越早不在最近3000ms内的请求里
- 满足先进先出,考虑用队列
var RecentCounter = function() {
this.queue = []
};
// 时间复杂度 O(n) n为剔出老请求的长度
// 空间复杂度 O(n) n为最近请求的次数
RecentCounter.prototype.ping = function(t) {
this.queue.push(t)
while(this.queue[0] < t - 3000) {
this.queue.shift();
}
return this.queue.length
};