第四章-数据结构之队列
队列简介
- 队列是一个 先进先出 的数据结构
- JavaScript 中没有队列,但可以使用 Array 实现队列的所有功能
比如 窗口排队,先排队的人先完成业务
const queue = [];
queue.push(1); // [1]
queue.push(2); // [1, 2]
let res1 = queue.shift(); // res1 = 1 queue = [2]
let res2 = queue.shift(); // res2 = 2 queue = []
队列的应用场景
- 需要先进先出的场景
- 比如:食堂排队打饭,js 异步中的任务队列、计算最近的请求次数
1. 食堂排队打饭
先进先出,保证有序
2. js 异步中任务队列
- JS 是单线程,无法同时处理异步中的并发任务,【事件循环】
- 使用任务队列先后处理异步任务
3. 计算最近请求次数
- 有新请求就入队,3000ms 前发出的请求出队
- 队列的长度就是最近的请求次数
LeetCode 933. 最近的请求次数
【题目描述】
-
写一个 RecentCounter 类来计算特定时间范围内最近的请求。
-
请你实现 RecentCounter 类:
RecentCounter() 初始化计数器,请求数为 0 。
int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。
保证每次对 ping 的调用都使用比之前更大的 t 值。 -
输入: ["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 = [<u>1</u>, <u>100</u>],范围是 [-2900,100],返回 2 recentCounter.ping(3001); // requests = [<u>1</u>, <u>100</u>, <u>3001</u>],范围是 [1,3001],返回 3 recentCounter.ping(3002); // requests = [1, <u>100</u>, <u>3001</u>, <u>3002</u>],范围是 [2,3002],返回 3
【解题思路】
- 每次执行的是 ping 这个方法,计算当前时间内 ping 这个方法的有效个数
- 越早发出的请求越早不在最近 3000ms 内的请求里
- 满足先进先出,考虑用队列
【解题步骤】
- 有新请求就入队,3000ms 前发出的请求就出队
- 队列的长度就是最近请求次数
【复杂度计算】
- 时间复杂度:O(n) --- while 循环中出队的请求数
- 空间复杂度:O(n) --- 新建的 queue 本质上是一个数组
【答案】
var RecentCounter = function() {
this.queue = [];
};
/**
* @param {number} t
* @return {number}
*/
RecentCounter.prototype.ping = function(t) {
// t 是当前请求的时刻
this.queue.push(t);
// 如果第一个请求,在 3000ms 之前,那就将第一个请求出队
while (this.queue[0] < t - 3000) {
this.queue.shift();
}
// 每次都返回当前调用 ping 的队列的长度
return this.queue.length;
};
/**
* Your RecentCounter object will be instantiated and called as such:
* var obj = new RecentCounter()
* var param_1 = obj.ping(t)
*/
JS 异步中的任务队列
先返回结果的任务队列先执行
异步面试题
setTimeout(() => {
console.log(1);
}, 0)
console.log(2);
//output: 2, 1
思考题
-
请用 ES6 的 class,封装一个 Queue 类,包括 push、shift、peek 方法。
class Queue { constructor() { this.queue = []; this.count = 0; } push (item) { this.queue = [...this.queue, item]; this.count++; return this.queue; } shift () { if (this.count === 0) return undefined; let remove = this.queue[0]; delete this.queue[0]; this.queue = this.queue.filter(item => item !== undefined); this.count--; return remove; } peek () { if (this.count === 0) return undefined; return this.queue[0] } } let queue = new Queue(); queue.push(1); queue.push(2); queue.push(3); queue.push(4); console.log(queue.push(5)); // [1,2,3,4,5] let res1 = queue.shift(); console.log(res1); // 1 console.log(queue.peek()); // 2
-
请用队列这个数据结构结合 React 或 Vue写一个任务 App,包括添加任务和完成任务功能,要求任务只能先进先出.
<div id="app"> <input type="text" v-model='task' @keyUp.13='addTask'> <button @click='addTask'>添加任务</button> <button @click='completeTask'>完成任务</button> <p>未完成的任务</p> <ul> <li v-for='(item, index) in taskList' :key='index'>{{item}}</li> </ul> <p>已完成的任务</p> <ul> <li v-for='(item, index) in completeTaskList' :key='index'>{{item}}</li> </ul> </div> <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script> <script> new Vue({ el: '#app', data: { taskList: [], task: '', completeTaskList: [] }, methods: { addTask() { if (this.task) { this.taskList.push(this.task); this.task = ''; } }, completeTask() { if (this.taskList.length > 0) { let taskTop = this.taskList.shift(); this.completeTaskList.push(taskTop); } } } }) </script>