队列是一种列表,不同的是队列只能在末尾插入元素,在队首删除元素。队列用于存储按顺序排列的数据。先进先出。这点和栈不一样,在栈中,最后入栈的元素反被优先处理。可以将队列想象成银行排队办理业务的人,排队在第一个的人先办理业务,其它人只能排着,直到轮到他们为止。

队列是一种先进先出(FIFO)的数据结构。队列被用在很多地方。比如提交操作系统执行一系列进程。打印任务池等。一些仿真系统用来模拟银行或杂货店里排队的顾客。

一,队列的操作。

队列的两种主要操作是:向队列中插入新元素和删除队列中的元素。插入操作也叫做入队。删除元素也叫做出队。入队是在末尾插入新元素,出队是删除队头的元素。

队列的另外一项操作是读取队头的元素,这个操作叫peek()。该操作返回队头的元素,但不把它从队列中删除。除了读取队头的元素,我们想知道队列中有多少元素,可以使用length属性满足要求;要想清空队列中所有元素。可以使用clear()方法来实现。

二,一个数组实现的队列。

使用数组来实现队列看起来顺理成章。javascript中的数组具有其它编程语言中没有的优点,数组的push()可以在数组末尾加入元素,数组的shift()方法则可以删除数组的第一个元素。

push()方法将它的参数插入数组中第一个开放的位置,该位置总在数组的末尾,即使是个空数组也是如此。

    names = [];
names.push("Cny");
names.push("Jen");
console.log(names);//["Cny", "Jen"]

然后使用shift()方法删除数组的第一个元素:

    names.shift();
console.log(names);//["Jen"]

准备开始实现Queue类。先从构造函数开始:

    function Queue() {
this.dataStore = [];
this.enqueue = enqueue;
this.dequeue = dequeue;
this.front = front;
this.back = back;
this.toString = toString;
this.empty = empty;
}

enqueue()方法向队末尾添加一个元素:

    function enqueue(element) {
this.dataStore.push(element)
}

dequeue方法删除队首的元素

    function dequeue() {
return this.dataStore.shift();
}

可以使用以下方法读取队首和队末的元素

    function front() {
return this.dataStore[0];
} function back() {
return this.dataStore[this.dataStore.length - 1]
}

还需要toString()方法显示队列内的所有元素

    function toString() {
var retStr = "";
for (var i = 0; i < this.dataStore.length; ++i ) {
retStr += this.dataStore[i] + "\n";
}
return retStr
}

最后,需要一个方法判断队列是否为空

    function empty() {
if (this.dataStore.length == 0) {
return true;
} else {
return false;
}
}

Queue类的定义和测试

    function Queue() {
this.dataStore = [];
this.enqueue = enqueue;
this.dequeue = dequeue;
this.front = front;
this.back = back;
this.toString = toString;
this.empty = empty;
} //向队末尾添加一个元素
function enqueue(element) {
this.dataStore.push(element)
} //删除队首的元素
function dequeue() {
return this.dataStore.shift();
} function front() { //读取队首和队末的元素
return this.dataStore[0];
}
function back() { ////读取队首和队末的元素
return this.dataStore[this.dataStore.length - 1]
} //显示队列内的所有元素
function toString() {
var retStr = "";
for (var i = 0; i < this.dataStore.length; ++i ) {
retStr += this.dataStore[i] + "\n";
}
return retStr
} //队列是否为空
function empty() {
if (this.dataStore.length == 0) {
return true;
} else {
return false;
}
} //测试程序 var q = new Queue();
q.enqueue("Me");
q.enqueue("Her");
q.enqueue("His");
console.log(q.toString());
q.dequeue();
console.log(q.toString()); console.log("第一个元素是: " + q.front());
console.log("最后一个元素是: " + q.back())

三,使用队列,方块舞和舞伴的分配问题

前面我们提到过,经常用队列模拟排队的人。下面,我们使用队列来模拟跳方块舞的人。当男男女女来到舞池,他们按照自己的性别排成两队。当舞池中有地方空出来的时候,两个队列中的第一个人组成舞伴。他们身后的人各自向前一位,组成新的队首。当一对新的舞伴进入舞池时,主持人会大喊出他们的名字。当一对舞伴走出舞池,且两队队伍任意一队没有人时,主持人会把这个情况告诉大家。

为了模拟这个情况,我们将男男女女姓名存储在一个文本文件。

女 小李
男 福来
男 强子
男 李勇
女 小梅
男 来福
女 艾丽
男 张帆
男 文强
男 丁力
女 娜娜

每个舞者的信息都被保存在一个Dancer对象中:

    function Dancer(name, sex) {
this.name = name;
this.sex = sex;
}

我们需要一个函数,将舞者的信息读到程序中来。

    function getDancers(males, females) {
//var names = _names.split("\n");
var names = _names.split("**");
for (var i = 0; i < names.length; ++i) {
names[i] = names[i].trim();
} for (var i = 0; i < names.length; ++i) {
var dancer = names[i].split(" ");
var sex = dancer[0];
var name = dancer[1];
if (sex == "女") {
females.enqueue(new Dancer(name, sex));
} else {
males.enqueue(new Dancer(name, sex))
}
}
}

此函数将舞者按照性别分在不同的队列中。

下一个函数将男性和女性组成舞伴,并宣布配对结果:

    function dance(males, females) {
console.log("这组舞伴是:")
while (!females.empty() && !males.empty()) {
person = females.dequeue();
console.log("女舞者是" + person.name); person = males.dequeue();
console.log("男舞者是" + person.name);
}
}

我们可能对该程序做修改,想显示排队时男性女性的数量,队列中目前没有显示元素个数的方法。现在加入

    function count(){
return this.dataStore.length;
}

综合测试:

    function Queue() {
this.dataStore = [];
this.enqueue = enqueue;
this.dequeue = dequeue;
this.front = front;
this.back = back;
this.toString = toString;
this.empty = empty;
this.count = count;
} //向队末尾添加一个元素
function enqueue(element) {
this.dataStore.push(element)
} //删除队首的元素
function dequeue() {
return this.dataStore.shift();
} function front() { //读取队首和队末的元素
return this.dataStore[0];
}
function back() { ////读取队首和队末的元素
return this.dataStore[this.dataStore.length - 1]
} //显示队列内的所有元素
function toString() {
var retStr = "";
for (var i = 0; i < this.dataStore.length; ++i ) {
retStr += this.dataStore[i] + "\n";
}
return retStr
} //队列是否为空
function empty() {
if (this.dataStore.length == 0) {
return true;
} else {
return false;
}
} //队列个数
function count(){
return this.dataStore.length;
} function Dancer(name, sex) {
this.name = name;
this.sex = sex;
} function getDancers(males, females) {
//var names = _names.split("\n");
var names = _names.split("**");
for (var i = 0; i < names.length; ++i) {
names[i] = names[i].trim();
} for (var i = 0; i < names.length; ++i) {
var dancer = names[i].split(" ");
var sex = dancer[0];
var name = dancer[1];
if (sex == "女") {
females.enqueue(new Dancer(name, sex));
} else {
males.enqueue(new Dancer(name, sex))
}
}
} function dance(males, females) {
console.log("这组舞伴是:")
while (!females.empty() && !males.empty()) {
person = females.dequeue();
console.log("女舞者是" + person.name); person = males.dequeue();
console.log("男舞者是" + person.name);
}
} //测试程序 var _names = "女 小李**男 福来**男 强子**男 李勇**女 小梅**男 来福**女 艾丽**男 张帆**男 文强**男 丁力**女 娜娜"; var names = _names.split("**"); //测试程序
var maleDancer = new Queue();
var femaleDancer = new Queue(); getDancers(maleDancer, femaleDancer);
dance(maleDancer, femaleDancer) if (!femaleDancer.empty()) {
console.log(femaleDancer.front().name + "正在等待跳舞")
} if (!maleDancer.empty()) {
console.log(maleDancer.front().name + "正在等待跳舞")
} //显示等待跳舞的人个数 var nanDancers = new Queue();
var nvDancers = new Queue();
getDancers(nanDancers, nvDancers)
dance(nanDancers,nvDancers);
if (nanDancers.count() > 0) {
console.log("有" + nanDancers.count() + "男舞者等待")
} if (nvDancers.count() > 0) {
console.log("有" + mvDancers.count() + "女舞者等待")
}

四:使用队列对数据进行排序

队列不仅用于执行显示生活中与排队有关的操作,还可以用于对数据进行排序。计算机刚出现时,程序是通过穿孔卡输入主机的,每张卡包含一条程序语句。这些穿孔卡装在一个盒子里,经过一个继续装置进行排序。我们可以使用一组队列来模拟这一过程。这种技术叫基数排序。它不是最快的排序算法,但是它展示了一些有趣的队列使用方法。

对于0-99的数字,基数排序将数据扫描两次。第一次按个位上的数字进行排序,第二次按照十位上的数字进行排序。每个数字根据对于位上的数值被分在不同的盒子里。假设有如下数字:

91,46,85,15,92,35,31,22

经过基数排序第一次扫描后,数字被分配到以下盒子里

Bin 0 :
Bin 1 : 91, 31
Bin 2 : 82, 22
Bin 3 :
Bin 4 :
Bin 5 : 85, 35
Bin 6 : 46
Bin 7 :
Bin 8 :
Bin 9 :

根据盒子的顺序,对数字第一次排序的结果如下

91,31,92,22,85,15,25,46

根据十位上的数字,再次排序。

Bin 0 :
Bin 1 : 15
Bin 2 : 22
Bin 3 : 31, 35
Bin 4 : 46
Bin 5 :
Bin 6 :
Bin 7 :
Bin 8 : 85
Bin 9 : 91, 92

最后,将盒子中的数字去除,组成一个新的列表,该列表即为排好序的数字:

15, 22, 31, 35, 46, 85, 91, 92

使用队列代表盒子,可以实现这个算法。我们需要9个队列,每一个对应一个数字。所有队列保存在一个数组中,使用取余和除法操作决定个位和十位。算法的剩余部分将数字加入相应队列,根据个位数值对其从新排序,然后根据十位上的数值进行排序。结果即为排列好的数字

下面是根据 相应位(个位或十位)上的数值,将数字分配到相应队列的函数:

    function distribute(nums, queues, n, digit) { //digit表示个位和十位上的值
for (var i = 0 ; i < n ; ++i) {
if (digit == 1) {
queues[nums[i]%10].enqueue(nums[i]);
} else {
queues[Math.floor(nums[i]/10).enqueue(nums[i])]
}
}
}

下面是从队列中收集数字的函数

    function collect(queues, nums){
var i = 0;
for (var digit = 0; digit < 10; ++digit) {
while(!queues[digit].empty()){
nums[i++] = queues[digit].dequeue()
}
}
}

附上测试代码

    function Queue() {
this.dataStore = [];
this.enqueue = enqueue;
this.dequeue = dequeue;
this.front = front;
this.back = back;
this.toString = toString;
this.empty = empty;
this.count = count;
} //向队末尾添加一个元素
function enqueue(element) {
this.dataStore.push(element)
} //删除队首的元素
function dequeue() {
return this.dataStore.shift();
} function front() { //读取队首和队末的元素
return this.dataStore[0];
}
function back() { ////读取队首和队末的元素
return this.dataStore[this.dataStore.length - 1]
} //显示队列内的所有元素
function toString() {
var retStr = "";
for (var i = 0; i < this.dataStore.length; ++i ) {
retStr += this.dataStore[i] + "\n";
}
return retStr
} //队列是否为空
function empty() {
if (this.dataStore.length == 0) {
return true;
} else {
return false;
}
} //队列个数
function count(){
return this.dataStore.length;
} //基础排序程序
function distribute(nums, queues, n, digit) { //digit表示个位和十位上的值
for (var i = 0 ; i < n ; ++i) {
if (digit == 1) {
queues[nums[i]%10].enqueue(nums[i]);
} else {
queues[Math.floor(nums[i] / 10)].enqueue(nums[i])
}
}
} function collect(queues, nums){
var i = 0;
for (var digit = 0; digit < 10; ++digit) {
while(!queues[digit].empty()){
nums[i++] = queues[digit].dequeue()
}
}
} function dispArray(arr) {
for (var i = 0; i < arr.length; ++i) {
console.log(arr[i] + " ")
}
} //主程序 var queues = []
for (var i = 0; i < 10; ++i) {
queues[i] = new Queue();
}
var nums = []
for (var i = 0; i < 10; ++i){
nums[i] = Math.floor(Math.floor(Math.random() * 101))
} console.log("之前的基数:")
dispArray(nums);
distribute(nums, queues, 10, 1);
collect(queues, nums);
distribute(nums,queues, 10 ,10);
collect(queues, nums);
console.log("之后的基数:")
dispArray(nums)

五:优先队列方法

在一般的情况下,从队列中删除的元素,一定是率先入队的元素。但是也有一些使用队列的应用 ,在删除元素时不必遵守先进先出的约定。这周应用,需要使用一个叫优先队列的数据结构来进行模拟。

从优先队列中删除元素时,需要考虑优先权的限制。比如医院急诊科的候诊室,就是一个采用优先队列的例子。当病人进入候诊室,分诊会评估病情的严重程度,然后给一个优先级代码。高优先级的患者优先于低优先级者就医,同样优先级的患者按照优先来服务的顺序就医。

先来定义存储队列元素的对象,然后再构建我们的优先队列系统。

    function Patient(name, code){
this.name = name;
this.code = code;
}

变量code是一个整数,表示优先级

现在需要从新定义dequeue()方法。使其删除队列中优先级最高级的元素。我们规定:优先码的值最小的元素优先级最高。新的dequeue()方法遍历队列底层存储数组,从中找出优先码最低的元素,然后使用数据的splice()方法删除优先级最高的元素。新的dequeue()方法定义如下所示:

    function dequeue() {
var priority = this.dataStore[0].code;
for (var i = 1; i < this.dataStore.length; ++i) {
if (this.dataStore.[i].code < priority) {
priority = i
}
}
return this.dataStore.splice(priority,1)
}

dequeue()方法使用简单的顺序查找方法寻找优先级最高的元素(优先码最小优先级越高,比如1比5的优先级高)该方法返回包含一个元素的数组---从队列中删除的元素。
最后,定义toString()来显示patient对象

    function toString() {
var retStr = "";
for (var i=0; i < this.dataStore.length; ++i) {
retStr += this.dataStore[i].name + " code:" + this.dataStore[i].code + "\n"
}
return retStr
}

(本文完结)

上一章:第四章:javascript: 栈 下一章第六章: 字典

第五章:javascript:队列的更多相关文章

  1. 第五章 JavaScript对象及初识面向对象

    第五章   JavaScript对象及初识面向对象 一.对象 在JavaScript中,所有事物都是对象,如字符串.数值.数组.函数等. 在JavaScript对象分为内置对象和自定义对象,要处理一些 ...

  2. 学习笔记 第十五章 JavaScript基础

    第15章   JavaScript基础 [学习重点] 了解JavaScript基础知识 熟悉常量和变量 能够使用表达式和运算符 正确使用语句 能够掌握数据类型和转换的基本方法 正确使用函数.对象.数组 ...

  3. 第五章JavaScript

    创建数组://1.字面量方式创建 (推荐大家使用这种方式创建数组 简单粗暴) var colors = ['red','color','yellow'];console.log(colors) //空 ...

  4. 第五章 javascript编程可养成的好习惯

    用户点击某个链接时弹出一个新窗口javascript使用window对象的open()方法来创建新的浏览器窗口,这个方法有三个参数:window.open(url,name,features)url: ...

  5. 为什么我要放弃javaScript数据结构与算法(第五章)—— 链表

    这一章你将会学会如何实现和使用链表这种动态的数据结构,这意味着我们可以从中任意添加或移除项,它会按需进行扩张. 本章内容 链表数据结构 向链表添加元素 从链表移除元素 使用 LinkedList 类 ...

  6. 读《编写可维护的JavaScript》第五章总结

    第五章 UI层的松耦合 5.1 什么是松耦合 在Web开发中,用户界面是由三个彼此隔离又相互作用的层定义的: HTML是用来定义页面的数据和语义 CSS用来给页面添加样式 JavaScript用来给页 ...

  7. Javascript权威指南——第二章词法结构,第三章类型、值和变量,第四章表达式和运算符,第五章语句

    第二章 词法结构 一.HTML并不区分大小写(尽管XHTML区分大小写),而javascript区分大小写:在HTML中,这些标签和属性名可以使用大写也可以使用小写,而在javascript中必须小写 ...

  8. ArcGIS API for JavaScript 4.2学习笔记[16] 弹窗自定义功能按钮及为要素自定义按钮(第五章完结)

    这节对Popups这一章的最后两个例子进行介绍和解析. 第一个[Popup Actions]介绍了弹窗中如何自定义工具按钮(名为actions),以PopupTemplate+FeatureLayer ...

  9. [书籍翻译] 《JavaScript并发编程》第五章 使用Web Workers

    本文是我翻译<JavaScript Concurrency>书籍的第五章 使用Web Workers,该书主要以Promises.Generator.Web workers等技术来讲解Ja ...

  10. 第十五章、线程之queue模块的各种队列

    目录 第十五章.线程之queue模块的各种队列 一.Queue 二.LifoQueue堆栈 三.PriorityQueue优先级队列 第十五章.线程之queue模块的各种队列 一.Queue impo ...

随机推荐

  1. 获取某地的经纬度 && 通过经纬度获取相应的地理位置

    最近要通过一个经纬度判断该经纬度是否位于某个地区内,所以通过网上查找资料,整合后出了下面的内容. 1.通过地址获取改地址的经纬度 /** * @param addr * 查询的地址 * @return ...

  2. Icacls 在windows目录文件授权中的应用

    前言 最近因工作需要,需要对批量服务器某一目录下的文件进行统一授权,对于linux来说,授权很方便,对于window来说,要对目录下的文件进行批量授权还是很不方便的,windows平台授权自然想到用i ...

  3. Storm集群的安装与测试

    首先安装zookeeper集群,然后安装storm集群. 我使用的是centos 32bit的三台虚拟机. MachineName ip namenode 192.168.99.110 datanod ...

  4. 读《深入理解Java虚拟机》有感——第二部分:虚拟机类加载机制

    一.类加载过程       执行时机:编译程序——>执行程序(JVM启动.程序运行),类加载发生在程序运行期间       各个阶段:分为加载阶段.连接阶段(验证.准备.解析).初始化.使用.卸 ...

  5. 边工作边刷题:70天一遍leetcode: day 75-2

    Strobogrammatic Number I/II/III 要点:记题,注意轴对称和点对称的区别.这题就是几个固定digit之间的palindrome I https://repl.it/CqLu ...

  6. 如何解决inline-block元素的空白间距

    早上在博客中有人提了这样一个问题:“li元素inline-block横向排列,出现了未知间隙”,我相信大家在写页面的时候都遇到过这样的情况吧. 我一般遇到这情况都会把li浮动起来,这样就没有间隙.但是 ...

  7. 两道相似KMP题

    1.POJ 3450 Coporate Identity 这两题的解法都是枚举子串,然后匹配,像这种题目以后可以不用KMP来做,直接字符串自带的strstr函数搞定,如果字符串未出现,该函数返回NUL ...

  8. AC日记——阶乘之和 洛谷 P1009(高精度)

    题目描述 用高精度计算出S=1!+2!+3!+…+n!(n≤50) 其中“!”表示阶乘,例如:5!=5*4*3*2*1. 输入输出格式 输入格式: 一个正整数N. 输出格式: 一个正整数S,表示计算结 ...

  9. QTP基础学习(一)安装目录介绍

    上一篇介绍了QTP 10 安装,安装完成后就可以看到文件的目录了,这里主要介绍以下几个目录及作用. 简单介绍部分目录 1.addins:插件包 2.bin目录:可执行程序,这里存储了很多配置文件.运行 ...

  10. 判断一个值是否在数组里,可以检测数字,字符串,json对象

    Array.prototype.indexOf = function (val) {//判断数组是否存在某个值,如果存在返回该值对应的索引,否则返回-1 for (var i = 0; i < ...