队列与栈不同,它遵从先进先出(FIFO——First In First Out)原则,新添加的元素排在队列的尾部,元素只能从队列头部移除。

  我们在前一篇文章中描述了如何用JavaScript来实现栈这种数据结构,这里我们对应地来实现队列。

function Queue() {
let items = []; // 向队列添加元素(一个或多个)
this.enqueue = function (element) {
if (element instanceof Array) items = items.concat(element);
else items.push(element);
}; // 从队列移除元素
this.dequeue = function () {
return items.shift();
}; // 返回队列中的第一个元素
this.front = function () {
return items[0];
}; // 判断队列是否为空
this.isEmpty = function () {
return items.length === 0;
}; // 返回队列的长度
this.size = function () {
return items.length;
}; // 清空队列
this.clear = function () {
items = [];
}; // 打印队列内的所有元素
this.print = function () {
console.log(items.toString());
};
}

  与栈的实现方式类似,唯一不同的是从队列移除元素时取的是队列头部的元素(最先添加的),而栈则是取的顶部元素(最后添加的)。下面是一些测试用例及返回结果:

let queue = new Queue();
console.log(queue.isEmpty()); // true queue.enqueue('John');
queue.enqueue(['Jack', 'Camila']);
queue.print(); // John,Jack,Camila
console.log(queue.size()); //
console.log(queue.isEmpty()); // false
console.log(queue.front()); // John console.log(queue.dequeue()); // John
queue.print(); // Jack,Camila queue.clear();
queue.print(); //

  注意,我们允许批量向队列中添加元素,为此我们需要判断enqueue方法的参数类型,如果参数是数组,则用concat()函数连接两个数组,如果参数不是数组,则直接用push()函数将元素添加到队列中。

  与栈的实现方式一样,这里我们也同样给出用ES6的WeakMap类来实现的队列版本。

let Queue = (function () {
const items = new WeakMap(); class Queue {
constructor() {
items.set(this, []);
} enqueue (element) {
let q = items.get(this);
if (element instanceof Array) items.set(this, q.concat(element));
else q.push(element);
}; dequeue () {
let q = items.get(this);
return q.shift();
}; front () {
return items.get(this)[0];
}; isEmpty () {
return items.get(this).length === 0;
}; size () {
return items.get(this).length;
}; clear () {
items.set(this, []);
}; print () {
console.log(items.get(this).toString());
};
} return Queue;
})();

  这两个版本的执行结果是一样的,它们的区别我们在前一篇文章中已经提及过了,这里不再赘述。

优先队列

  所谓优先队列,顾名思义,就是说插入到队列中的元素可以根据优先级设置先后顺序。优先级越高位置越靠前,优先级越低位置越靠后。假设优先级用数字来表示,如果数字越小表示的优先级越高,形成的队列就称之为最小优先队列,反之则称之为最大优先队列。下面是实现的代码:

function PriorityQueue() {
let items = []; // 向队列添加元素(一个或多个)
// 参数obj的数据格式:{element, priority}
this.enqueue = function (obj) {
if (obj instanceof Array) {
for (let i = 0, ci; ci = obj[i]; i++) {
this.enqueue(ci);
}
}
else {
let added = false;
for (let i = 0, ci; ci = items[i]; i++) {
// 最小优先级,即将priority值小的元素插入到队列的前面
if (obj.priority < ci.priority) {
items.splice(i, 0, obj);
added = true;
break;
}
} // 如果元素没有插入到队列中,则默认加到队列的尾部
if (!added) items.push(obj);
}
}; // 从队列移除元素
this.dequeue = function () {
return items.shift();
}; // 返回队列中的第一个元素
this.front = function () {
return items[0];
}; // 判断队列是否为空
this.isEmpty = function () {
return items.length === 0;
}; // 返回队列的长度
this.size = function () {
return items.length;
}; // 清空队列
this.clear = function () {
items = [];
}; // 打印队列内的所有元素
this.print = function () {
items.forEach(function (item) {
console.log(`${item.element} - ${item.priority}`);
});
};
}

  可以看到,唯一有区别的只有enqueue方法。我们规定所有添加到优先队列的元素都必须满足{element, priority}这种JSON格式,以保证队列中的每一个元素都有一个priority属性来表示优先级。如果要添加的元素的优先级和队列中已有元素的优先级相同,仍然遵循队列的先进先出原则。如果队列中所有元素的优先级比要添加的元素的优先级都高,则将元素添加到队列的末尾。我们将print()方法也做了一些调整,以方便查看输出结果。

let queue = new PriorityQueue();
console.log(queue.isEmpty()); // true queue.enqueue({element: 'John', priority: 2});
queue.enqueue([{element: 'Jack', priority: 1}, {element: 'Camila', priority: 1}]);
queue.print(); // Jack,Camila,John

  由于John的优先级比其它两个低,所以它被排在了最后面。虽然Jack和Camila的优先级相同,但是Jack是在Camila之前先插入到队列中的,所以Jack排在了Camila之前,这也符合了我们的预期。

循环队列

  我们用一个小游戏“击鼓传花”来说明循环队列在实际中的应用。

function hotPotato(nameList, num) {
let queue = new Queue(); for (let i = 0, ci; ci = nameList[i]; i++) {
queue.enqueue(ci);
} let eliminated = '';
while(queue.size() > 1) {
for (let i = 0; i < num; i ++) {
queue.enqueue(queue.dequeue());
}
eliminated = queue.dequeue();
console.log(`${eliminated} has been eliminated.`);
} return queue.dequeue();
} let names = ['John', 'Jack', 'Camila', 'Ingrid', "Carl"];
let winner = hotPotato(names, 7);
console.log(`The winner is: ${winner}`);

  在这个游戏中,我们传入由五个名字组成的数组,用来表示参加游戏的五个人,数字7表示每一轮要传递的次数。在每一个过程中,我们从队列头部取出一个元素加到队列的尾部,当次数用完的时候,将队列头部的元素取出来,作为这一轮中被淘汰的人。让我们来看一下具体的执行过程,一开始队列中的顺序是John, Jack, Camila, Ingrid, Carl,然后传递7次:

  1. Jack, Camila, Ingrid, Carl, John

  2. Camila, Ingrid, Carl, John, Jack

  3. Ingrid, Carl, John, Jack, Camila

  4. Carl, John, Jack, Camila, Ingrid

  5. John, Jack, Camila, Ingrid, Carl

  6. Jack, Camila, Ingrid, Carl, John

  7. Camila, Ingrid, Carl, John, Jack

  之后从队列中取出的是Camila。反复执行上述过程,直到队列中的元素只剩一个,这个就是最后的赢家!

  下面是完整的执行结果:

Camila has been eliminated.
Jack has been eliminated.
Carl has been eliminated.
Ingrid has been eliminated.
The winner is: John

  下一章我们继续来看看如何用JavaScript来实现链表。

JavaScript数据结构——队列的实现与应用的更多相关文章

  1. JavaScript数据结构——队列的实现

    前面楼主简单介绍了JavaScript数据结构栈的实现,http://www.cnblogs.com/qq503665965/p/6537894.html,本次将介绍队列的实现. 队列是一种特殊的线性 ...

  2. javascript数据结构——队列

    队列是一种先进先出的数据结.队列只能在队尾插入元素,在队首删除元素,这点和栈不一样.它用于存储顺序排列的数据.队列就像我们日常中的排队一样,排在最前面的第一个办理业务,新来的人只能在后面排队.队列这种 ...

  3. javascript数据结构-队列

    gihub博客地址 队列(Queue)是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表.进行插 ...

  4. JavaScript 数据结构与算法之美 - 线性表(数组、栈、队列、链表)

    前言 基础知识就像是一座大楼的地基,它决定了我们的技术高度. 我们应该多掌握一些可移值的技术或者再过十几年应该都不会过时的技术,数据结构与算法就是其中之一. 栈.队列.链表.堆 是数据结构与算法中的基 ...

  5. JavaScript数据结构——图的实现

    在计算机科学中,图是一种网络结构的抽象模型,它是一组由边连接的顶点组成.一个图G = (V, E)由以下元素组成: V:一组顶点 E:一组边,连接V中的顶点 下图表示了一个图的结构: 在介绍如何用Ja ...

  6. 学习javascript数据结构(一)——栈和队列

    前言 只要你不计较得失,人生还有什么不能想法子克服的. 原文地址:学习javascript数据结构(一)--栈和队列 博主博客地址:Damonare的个人博客 几乎所有的编程语言都原生支持数组类型,因 ...

  7. 数据结构与算法JavaScript (二) 队列

    队列是只允许在一端进行插入操作,另一个进行删除操作的线性表,队列是一种先进先出(First-In-First-Out,FIFO)的数据结构 队列在程序程序设计中用的非常的频繁,因为javascript ...

  8. javascript数据结构与算法---队列

    javascript数据结构与算法---队列 队列是一种列表,不同的是队列只能在队尾插入元素,在队首删除元素.队列用于存储按顺序排列的数据,先进先出,这点和栈不一样(后入先出).在栈中,最后入栈的元素 ...

  9. JavaScript数据结构——栈和队列

    栈:后进先出(LIFO)的有序集合 队列:先进先出(FIFO)的有序集合 --------------------------------------------------------------- ...

随机推荐

  1. WPF 入门笔记之控件内容控件

    一.控件类 在WPF中和用户交互的元素,或者说.能够接受焦点,并且接收键盘鼠标输入的元素所有的控件都继承于Control类. 1. 常用属性: 1.1 Foreground:前景画刷/前景色(文本颜色 ...

  2. PostgreSQL 窗口函数 ( Window Functions ) 如何使用?

    一.为什么要有窗口函数 我们直接用例子来说明,这里有一张学生考试成绩表testScore: 现在有个需求,需要查询的时候多出一列subject_avg_score,为此科目所有人的平均成绩,好跟每个人 ...

  3. druid一步到位

    版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons) 在配置application.yml文件的时候,原本写的是MySQL的连 ...

  4. 【dfs基础讲解及例题】

    定义 DFS(Depth-First-Search)深度优先搜索算法,是搜索算法的一种. 接下来因为懒得去找大段大段深奥的材料 所以就是一些个人的理解. 所谓深搜,是相对于广搜(只是第一篇)来说的.深 ...

  5. Git的使用和配置小白必看都是干货,为您解惑

    Git安装 首先下载git这个软件,然后打开码云新建仓库 在本地选择一个路径作为本地仓库 点新建仓库然后输入邮箱和密码,然后进行配置 在要作为本地仓库的地方新建一个文件夹,保存后关闭,在文件夹空白处鼠 ...

  6. springboot序

    springboot序 1.写在前面 (1) 前段时间把文章分了下类(说的是专栏,谈不上),分了三类:springboot.springcloud.mpp数据库greenplum,后来给springc ...

  7. [蓝桥杯] Fibonacci数列 入门

    原题链接 import java.util.Scanner;//导入Scanner类 public class Main { public static void main(String[] args ...

  8. [sublime3] 在linux下的终端中使用sublime3打开文件

    通过ln命令创建软连接实现 echo $PATH 查看路径 例 我的路径是: /home/rh/anaconda3/bin:/home/rh/bin:/home/rh/.local/bin:/usr/ ...

  9. TP框架基础(四)----添加数据

    [数据添加] add() 该方法返回被添加的新记录的主键id值 两种方式实现数据添加 1. 数组方式数据添加 $goods = D(“Goods”); $arr = array(‘goods_name ...

  10. 解决Oracle.DataAccess.Client.OracleConnection”的类型初始值设定项引发异常。

    解决Oracle.DataAccess.Client.OracleConnection”的类型初始值设定项引发异常. 这个问题他们说是oracle的版本问题 但是好像不是...(我感觉VS版本问题,我 ...