持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
一个链表结构,上一期有用到这个库。这次详细讲解这个库。
Tiny queue data structure
You should use this package instead of an array if you do a lot of Array#push() and Array#shift() on large arrays, since Array#shift() has linear time complexity O(n) while Queue#dequeue() has constant time complexity O(1). That makes a huge difference for large arrays.
A queue is an ordered list of elements where an element is inserted at the end of the queue and is removed from the front of the queue. A queue works based on the first-in, first-out (FIFO) principle.
微小队列数据结构
如果在大型数组上执行大量数组#push()和数组#shift(),则应该使用此包而不是数组,因为数组#shift()具有线性时间复杂度O(n),而队列#dequeue()具有恒定的时间复杂度O(1)。这对大型阵列有很大的影响。
队列是元素的有序列表,其中一个元素插入到队列的末尾,然后从队列的前面移除。队列基于先进先出(FIFO)原则工作。
Usage
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());
//=> '🌈'
API
队列 先进先出
queue = new Queue()
The instance is an Iterable, which means you can iterate over the queue front to back with a “for…of” loop, or use spreading to convert the queue to an array. Don't do this unless you really need to though, since it's slow.
.enqueue(value)入队
Add a value to the queue.
.dequeue()出队
Remove the next value in the queue.
Returns the removed value or undefined if the queue is empty.
.clear()清空
Clear the queue.
.size长度
The size of the queue.
参考
源码
// 链上节点构造函数 有value 值 和 next 指向下一个节点
class Node {
value;
next;
constructor(value) {
this.value = value;
}
}
export default class Queue {
// 有头、尾、长度
#head;
#tail;
#size;
// 构造时,初始化一下上面三个变量
constructor() {
this.clear();
}
// 新节点 入队,构造节点,
//如果有头节点,尾节点后接这个新节点,尾结点更新
//如果没头节点,把新节点赋值给头尾节点
//长度+1
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++;
}
//出队,从头出
// 如果没头,那就返回空
// 有头,拿到头值返回,头更新到后面的节点,长度-1
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;
}
get size() {
return this.#size;
}
// 定义了迭代器,使得链表允许遍历,
* [Symbol.iterator]() {
let current = this.#head;
while (current) {
// yield 通过 next 继续执行,或者for-of,解构。
yield current.value;
current = current.next;
}
}
// 参考测试用例
// const queue = new Queue();
// queue.enqueue('🦄');
// queue.enqueue('🌈');
// t.deepEqual([...queue], ['🦄', '🌈']);
}
链表和数组的区别,时间复杂度
具体看数组shitf和链表dequeue的区别
看dequeue源码实现,可以发现,只是改了一下链头的指向,没有其他节点 ,操作一次,o(1)
数组shitf我虽然不知道源码,但也知道,把队头拿出后,其他元素都要遍历往前挪一位,很明显复杂度是o(n)
同理插入亦是如此,当前这个链表库没有插入的功能,所以需要用插入的话就要用其他链表库了。
如最开头所述,操作大型数组时,若用到推出队头的操作,用链表更好,这大概也就是上一期并发任务用链表的原因。
测试时用到的库
- 代码风格,不一样就报错,不让执行。
- 检查类型、Check TypeScript type definitions
- 测试