背景
前端没有队列这种数据结构,而我们一般使用数组来进行模拟
但是如果在大型数组上执行大量数组#push()和数组#shift(),则应该使用链表而不是数组,因为数组#shift()具有线性时间复杂度O(n),而队列#dequeue()具有恒定的时间复杂度O(1)。这对于大型阵列来说有很大的不同。
队列
队列是元素的有序列表,其中一个元素插入到队列的末尾,并从队列的前面移除。队列基于先进先出(FIFO)原则工作。
yocto-queue
用法
import Queue from 'yocto-queue';
const queue = new Queue();
queue.enqueue('🦄');
queue.enqueue('🌈');
console.log(queue.size);
//=> 2
console.log(...queue);
//=> '🦄 🌈'
console.log(queue.dequeue());
//=> '🦄'
console.log(queue.dequeue());
//=> '🌈'
源码
Node类
class Node {
value;
next;
constructor(value) {
this.value = value;
}
}
node 类的作用就是用来定义一个典型链表,有value, next两个字段,通过next指向下一个节点//
Queue 类
// 定义头指针,尾指针,队列长度
#head;
#tail;
#size;
constructor() {
// 定义清空方法
this.clear();
}
// 定义入队方法
enqueue(value) {
// 接受入参创建新的链表节点
const node = new Node(value);
if (this.#head) {
// 队列存在时,根据先进先出插入队列末尾
this.#tail.next = node;
this.#tail = node;
} else {
// 队列不存在时,插入节点为头节点
this.#head = node;
this.#tail = node;
}
// 队列长度加一
this.#size++;
}
// 定义出队方法
dequeue() {
// 获取当前队列,不存在直接返回
const current = this.#head;
if (!current) {
return;
}
// 队列先进先出,将头节点指向下一节点,长度减一,并返回该队列
this.#head = this.#head.next;
this.#size--;
return current.value;
}
// 清空方法,直接将数据重置
clear() {
this.#head = undefined;
this.#tail = undefined;
this.#size = 0;
}
// 长度方法,直接返回size
get size() {
return this.#size;
}
// Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被 for...of 循环使用。
// * yield 为generator 函数,内部实现迭代方法
// 这也是为什么链表能使用 ... 方法和 for of 展开。
* [Symbol.iterator]() {
let current = this.#head;
while (current) {
yield current.value;
current = current.next;
}
}
}
总结
使用链表相较于数组的性能更好,数组的 #shift()具有线性时间复杂度O(n),而队列#dequeue()具有恒定的时间复杂度O(1);
在实现功能时,不仅要考虑把功能实现,更要注意性能问题。