有一天,当回顾本身走过的路时,你会发现这些奋斗不息的岁月,才是最美好的人生。——弗洛伊德
队列,英文 First In First Out 简称 FIFO,听从先进先出的原则,与 “栈” 相反,在队列的尾部添加元素,在队列的头部删除元素,若是队列中没有元素就称为空队列。node
队列对应到生活场景中有不少例子,例如,咱们去火车站窗口购票总要排队,先排队的人先购票,有新的人来了则在队尾排队等待前面的完成了依次购票。另外咱们的订单超时队列、活动抢购先到先得等等,队列在生活中应用很普遍。git
做者简介:五月君,Nodejs Developer,热爱技术、喜欢分享的 90 后青年,公众号「Nodejs技术栈」,Github 开源项目 https://www.nodejs.redgithub
JavaScript 中提供的数组功能便可实现一个简单的队列,使用起来也很方便,熟悉相关 API 便可,下面咱们来看下基于 JS 数组的入队、出队过程实现。算法
以上图片展现了队列的初始化、入队、出队过程,下面咱们采用 JavaScript 原型链的方式实现。数组
初始化队列数据结构
初始化一个存储队列中元素的数据结构,若是未传入默认赋值空数组,传入需先校验类型是否正确。函数
function QueueStudy(elements) { if (elements && !(elements instanceof Array)) { throw new Error('必须为数组格式!'); } this.elements = elements || []; }
队列添加元素学习
实现一个 enQueue 方法,向队列添加元素,注意只能是队列尾部添加,使用 JavaScript 数组中的 push 方法。测试
QueueStudy.prototype.enQueue = function(element) { this.elements.push(element); }
队列移除元素ui
实现一个 deQueue 方法,向队列头部弹出元素,使用 JavaScript 数组中的 shift 方法。
QueueStudy.prototype.deQueue = function() { return this.elements.shift(); }
经过 JavaScript 数组实现是很简单的,源码参见 https://github.com/Q-Angelo/project-training/tree/master/algorithm/queue-js.js
优先队列,元素的添加、删除是基于优先级进行的。一个现实的例子就是机场登机的顺序。头等舱和商务舱乘客的优先级要高于经济舱乘客。在有些国家,老年人和孕妇(或带小孩的妇女)登机时也享有高于其余乘客的优先级。
优先队列对应到咱们生活场景中也有不少例子,例如咱们去银行办理业务,通常都会排号先到的先办理,可是呢,还会有 VIP 会员优先办理,又或者去火车站窗口上购票也会有提示军人能够优先办理等等
实现步骤
核心实现继 JavaScript 数组实现队列的例子,对入队函数进行改造以下所示:
代码示例
PriorityQueue.prototype.enQueue = function(element, priority) { const queueElement = { element, priority }; if (this.isEmpty()) { return this.elements.push(queueElement); } let added = false; for (let i=0; i < this.elements.length; i++) { if (priority < this.elements[i]['priority']) { added = true; this.elements.splice(i, 0, queueElement) break; } } if (!added) { this.elements.push(queueElement); } } PriorityQueue.prototype.print = function() { console.log(this.elements.map(item => item.element).join(' | ')); }
运行测试
const queue = new PriorityQueue(); queue.enQueue('普通会员1', 5); queue.enQueue('普通会员2', 10); queue.print() // 普通会员1 | 普通会员2 queue.enQueue('VIP会员1', 3); queue.print() // VIP会员1 | 普通会员1 | 普通会员2 queue.enQueue('VIP会员2', 3); queue.print() // VIP会员1 | VIP会员2 | 普通会员1 | 普通会员2 queue.deQueue(); queue.print() // VIP会员2 | 普通会员1 | 普通会员2
图例展现
下面以图例的形式展现以上优先队列程序的运行过程
以上是将优先级最小的元素放置于队列前面,称之为最小优先队列,最大优先队列的实现则反之。源码参见 https://github.com/Q-Angelo/project-training/tree/master/algorithm/queue-priority.js
循环队列有些地方也称之为环形队列,其自己是一种环形结构的队列,相较于普通队列有个好处是第一个元素出队以后,剩下元素无需依次向前移位,充分利用了向量空间,在如下介绍中给出了完整的实现过程。
在设计环形队列时便可顺时针也可逆时针两个方向进行实现,在入队时可根据 (tail % capacity) 规则,进行队尾添加元素,tail 表示队尾的指针,capacity 表示容量,出队一样以(head % capacity)规则操做,head 表示队头指针,下面以长度为 6 的队列进行图文形式说明下实现过程。
ES6 实现循环队列
如下采用 EcameScript 6 的 Class 写法,实现一个环形队列,须要作哪些点呢?如下列出须要实现的功能点:
const Init = Symbol('QueueStudy#Init'); class QueueStudy { constructor (capacity) { if (!capacity) { throw new Error('The capacity field is required!'); } this.capacity = capacity; // 初始化容量 this[Init](); } /** * 清空队列,内存保留 */ clear() { this[Init]() } [Init]() { this.queue = new Array(this.capacity); // 初始化队列内存空间 this.queueLen = 0; // 初始化队列元素 this.head = 0; // 队头 this.tail = 0; // 尾部 } /** * 队列是否为空 */ isEmpty() { return this.queueLen === 0 ? true : false; } /** * 队列是否溢出 */ isOverflow() { return this.queueLen === this.capacity } /** * 入队 */ enQueue(element) { if (this.isOverflow()) { return false; } this.queue[this.tail] = element; this.tail++; this.tail = this.tail % this.capacity; this.queueLen++; return true; } /** * 出队 */ deQueue() { if (this.isEmpty()) { throw new Error('队列为空'); } else { const element = this.queue[this.head]; this.head++; // 队头位置移动 this.head = this.head % this.capacity; this.queueLen--; return element; } } /** * 队列长度 */ len() { return this.queueLen; } /** * 销毁队列,内存回收 */ destroy() { this.queue = null; } /** * 队列元素遍历 */ traversing() { console.log('------------traversing start------------'); for (let i=this.head; i<this.queueLen + this.head; i++) { console.log(this.queue[i % this.capacity]); } console.log('------------traversing end------------\n'); } }
运行测试
const q1 = new QueueStudy(6); q1.enQueue('a'); q1.traversing(); q1.enQueue('b'); q1.enQueue('c'); q1.enQueue('d'); q1.enQueue('e'); q1.enQueue('f'); q1.traversing(); console.log('出队: ', q1.deQueue()); q1.enQueue('g'); q1.traversing(); console.log('出队: ', q1.deQueue()); console.log('出队: ', q1.deQueue()); q1.enQueue('h'); console.log('出队: ', q1.deQueue()); console.log('出队: ', q1.deQueue()); console.log('出队: ', q1.deQueue()); q1.traversing(); q1.clear(); q1.traversing();
源码参见 https://github.com/Q-Angelo/project-training/tree/master/algorithm/queue-ring.js
以上就是队列的讲解,最开始讲解了在 JavaScript 中如何应用队列,同时也使用 JavaScript 数组提供的 API 功能实现了优先队列,最后介绍了从零开始如何实现一个环形队列,这个是重点,经过环形队列这个例子也能够帮助你们理解队列的基本实现机制是怎么样的,对环形队列这块不理解的建议多看几遍,总之多动手、多实践。
推荐我在学习数据结构中看的两本书 学习JavaScript数据结构与算法(第2版)、图解数据结构使用 Python 固然也不乏有其它更好的资源,供你们学习参考。