为什么我要放弃javaScript数据结构与算法(第四章)—— 队列
有两种结构类似于数组,但在添加和删除元素时更加可控,它们就是栈和队列。
第四章 队列
队列数据结构
队列是遵循FIFO(First In First Out,先进先出,也称为先来先服务)原则的一组有序的项。队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。
现实中,很常见的例子就是排队。在计算机科学里面是打印队列。
创建队列
我们需要创建自己的类来表示一个队列,先从最基本的声明开始:
function Queue(){
// 这里是属性和方法
}
首先需要一个用于存储队列中元素的数据结构。我们可以使用数组,就像上一章 Stack 类中那样使用(你会发现其实两者很相似,只是添加和移除元素不一样而已。)
let items = []
接下来需要声明一些队列可用的方法。
- enqueue(element(s)):向队列尾部添加一个(或多个)新的项。
- dequeue():移除队列中的第一个(排列在队伍最前面的)项,并返回被移除的元素
- front():返回队列中的第一个元素——最先被添加,也将是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息——与 Stack 类的 peek 方法非常相似)
- isEmpty():如果队列中不包含任何元素,返回 ture,否则返回 false
- size():返回队列包含的元素个数,与数组的 length 属性类似。
向队列添加元素
首先要实现的是 enqueue 方法。这个方法负责向队列中添加新元素,还有一个非常重要的细节,新的项目只能添加到队列末尾:
this.enqueue = function(element){
return items.push(element);
};
从队列中移除元素
接下来就是 dequeue 方法,这个方法负责从队列中移除项。由于队列遵循先进先出原则,最先添加的项也是要最先被移除的。数组中的 shift 方法会从数组中移除存储在索引0(第一个位置)的元素。
this.dequeue = function(element){
return items.shift();
}
只有 enqueue 方法和 dequeue 方法可以添加和移除元素,这样就确保了 Queue 类遵循先进先出的原则。
查看队列头元素
为我们类实现一些额外的辅助方法。我们想知道队列最前面是什么,可以使用 front 方法查看
this.front = function(){
return items[0];
}
检查队列是否为空
this.isEmpty = function(){
return items.length == 0;
}
查看队列的长度
this.size = function(){
return items.length;
}
打印队列元素
this.print = function(){
console.log(items.toString());
}
实例
function Queue(){
let items = [];
this.enqueue = function(element){
return 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(); // 新建 类 Queue 的实例 queue
console.log(queue.isEmpty()); // 队列没有元素,返回 true
queue.enqueue(5); // 先队列中加 5
queue.enqueue(8); // 先队列中加 8
queue.dequeue(); // 减去队列的开头
console.log(queue.front()); // 8
queue.enqueue(11); // 先队列中加 11
console.log(queue.size()); // 队列的长度 2
console.log(queue.isEmpty()); // 队列有元素,返回 false
queue.enqueue(15); // 先队列中加 15
queue.print(); // 输出队列中的元素 8,11,15
使用ES6 语法实现的 Queue 类
我们使用一个 WeakMap 来保存私有属性items,并用外层函数(闭包)来封装 Queue 类。
let Queue = (function(){
const items = new WeakMap(); // 声明了一个 WeakMap 类型的变量 items
class Queue{
constructor(){
items.set(this, []) // 在 constructor 中,以this(Stack类自己引用)为键,把代表栈的数组存入 items
}
enqueue(element){
let q = items.get(this);
q.push(element);
}
dequeue (){
let q = items.get(this);
let r = q.shift();
return r;
}
front (){
let q = items.get(this);
return q[0];
}
isEmpty (){
let q = items.get(this);
return q.length == 0;
}
size (){
let q = items.get(this);
let r = q.length
return r;
}
clear (){
items.set(this, [])
}
print (){
let q = items.get(this);
console.log(q.toString());
}
}
return Queue;
})();
优先队列
队列大量应用在计算机科学以及我们的生活中,其中一个就是优先队列。元素的添加和移除是基于优先级的。现实中的例子就是登机的顺序。头等舱和商务舱的乘客优先级要优于经济舱乘客。
另外一个现实的例子就是医院的候诊室。医生会优先处理病情比较严重的患者。
实现一个队列,有两种选项:设置优先级,然后在正确的位置添加元素;或者用入列操作添加元素,然后按照优先级操作它们。在这个实例中,我们会在正确的位置添加元素,因此可以对它们使用默认的出列操作。
function ProrityQueue(){
let items = [];
function QueueElement(element, priority){
// 参数包含了添加到队列的元素以及其的优先级
this.element = element;
this.priority = priority;
}
this.enqueue = function(element, priority){
let queueElement = new QueueElement(element, priority);
let added = false;
// 如果队列为空可以直接将元素插入,否则就要比较元素与该元素的优先级。
// 当找到一个比要添加元素的 priority 值更高(优先级更低)的项时,
// 我们就把元素插入它之前,但是如果优先级相同的话就遵循先进先出的原则
for(let i = 0; i < items.length; i++){
if(queueElement.priority < items[i].priority ){
items.splice(i,0,queueElement);
added = true;
break;
}
}
if(!added){
// 如果添加元素的 priority 值大于任何已有的元素,把它添加到队列的末尾就行了
items.push(queueElement);
}
}
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(){
for(let i = 0; i < items.length; i++){
console.log(`${items[i].element} - ${items[i].priority}`);
}
}
}
let prorityQueue = new ProrityQueue();
prorityQueue.enqueue('John',2);
prorityQueue.enqueue('Mike',1);
prorityQueue.enqueue('Jenny',1);
prorityQueue.print();
/*
Mike - 1
Jenny - 1
John - 2
*/
循环队列——击鼓传花
还有另一个修改版的队列实现,就是循环队列。循环队列的一个例子就是击鼓传花游戏(Hot Potato)。在这个游戏中,孩子们围成一个圆圈,把花尽快地传递给旁边的人,某一时刻传花停止,这个时候,花就在谁的手里,谁就退出圆圈结束游戏。重复这个过程,直到最后一个孩子,就是胜者。
在这个例子中,我们要实现一个模拟的击鼓传花游戏。
function Queue(){
let items = [];
this.enqueue = function(element){
return 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());
}
}
function hotPotata(nameList, num){
let queue = new Queue();
// 将姓名名单 nameList 逐个加入到队列中
for(let i = 0; i < nameList.length; i++){
queue.enqueue(nameList[i]);
}
// 给定一个数字,然后迭代队伍,从队列中开头移除一项
// 然后将其添加到队伍的末尾,模拟击鼓传花
// 一旦传递次数达到给定的数字,拿着花的那个人就被淘汰
let eliminated = '';
while(queue.size() > 1){
for(let i = 0; i < num; i++){
queue.enqueue(queue.dequeue());
}
eliminated = queue.dequeue();
console.log(eliminated+'在击鼓传花游戏中被淘汰');
}
return queue.dequeue();
}
let names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl'];
let winner = hotPotata(names, 7);
console.log('获胜者是'+ winner);
// Camila在击鼓传花游戏中被淘汰
// John在击鼓传花游戏中被淘汰
// Carl在击鼓传花游戏中被淘汰
// Jack在击鼓传花游戏中被淘汰
// 获胜者是Ingrid
下图模拟了这个输出过程:

可以改变传入 hotPotata 函数的数字,模拟不同的场景。
JavaScript 任务队列
当我们在浏览器中打开新标签时,就会创建一个任务队列。这是因为每个标签都是单线程处理所有的任务,它被称为 事件循环。浏览器要负责多个任务,如渲染 HTML ,执行 JavaScript 代码,处理用户交互(用户输入,鼠标点击等),执行和处理异步请求。
小结
这一章学习了队列这种数据结构。实现了自己的队列算法,学习了如何通过 enqueue 方法和 dequeue 方法添加和移除元素。还学习了两种非常著名的特殊队列的实现,优先队列和循环队列(使用击鼓传花的实现)
下一章,将学习链表,一种比数组更加复杂的数据结构。
书籍链接: 学习JavaScript数据结构与算法
为什么我要放弃javaScript数据结构与算法(第四章)—— 队列的更多相关文章
- 为什么我要放弃javaScript数据结构与算法(第九章)—— 图
本章中,将学习另外一种非线性数据结构--图.这是学习的最后一种数据结构,后面将学习排序和搜索算法. 第九章 图 图的相关术语 图是网络结构的抽象模型.图是一组由边连接的节点(或顶点).学习图是重要的, ...
- 为什么我要放弃javaScript数据结构与算法(第二章)—— 数组
第二章 数组 几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构.JavaScript里也有数组类型,虽然它的第一个版本并没有支持数组.本章将深入学习数组数据结构和它的能力. 为什么 ...
- 为什么我要放弃javaScript数据结构与算法(第一章)—— JavaScript简介
数据结构与算法一直是我算比较薄弱的地方,希望通过阅读<javaScript数据结构与算法>可以有所改变,我相信接下来的记录不单单对于我自己有帮助,也可以帮助到一些这方面的小白,接下来让我们 ...
- 为什么我要放弃javaScript数据结构与算法(第十一章)—— 算法模式
本章将会学习递归.动态规划和贪心算法. 第十一章 算法模式 递归 递归是一种解决问题的方法,它解决问题的各个小部分,直到解决最初的大问题.递归通常涉及函数调用自身. 递归函数是像下面能够直接调用自身的 ...
- 为什么我要放弃javaScript数据结构与算法(第十章)—— 排序和搜索算法
本章将会学习最常见的排序和搜索算法,如冒泡排序.选择排序.插入排序.归并排序.快速排序和堆排序,以及顺序排序和二叉搜索算法. 第十章 排序和搜索算法 排序算法 我们会从一个最慢的开始,接着是一些性能好 ...
- 为什么我要放弃javaScript数据结构与算法(第八章)—— 树
之前介绍了一些顺序数据结构,介绍的第一个非顺序数据结构是散列表.本章才会学习另一种非顺序数据结构--树,它对于存储需要快速寻找的数据非常有用. 本章内容 树的相关术语 创建树数据结构 树的遍历 添加和 ...
- 为什么我要放弃javaScript数据结构与算法(第七章)—— 字典和散列表
本章学习使用字典和散列表来存储唯一值(不重复的值)的数据结构. 集合.字典和散列表可以存储不重复的值.在集合中,我们感兴趣的是每个值本身,并把它作为主要元素.而字典和散列表中都是用 [键,值]的形式来 ...
- 为什么我要放弃javaScript数据结构与算法(第六章)—— 集合
前面已经学习了数组(列表).栈.队列和链表等顺序数据结构.这一章,我们要学习集合,这是一种不允许值重复的顺序数据结构. 本章可以学习到,如何添加和移除值,如何搜索值是否存在,也可以学习如何进行并集.交 ...
- 为什么我要放弃javaScript数据结构与算法(第五章)—— 链表
这一章你将会学会如何实现和使用链表这种动态的数据结构,这意味着我们可以从中任意添加或移除项,它会按需进行扩张. 本章内容 链表数据结构 向链表添加元素 从链表移除元素 使用 LinkedList 类 ...
随机推荐
- web.config如何实现301跳转
.htaccess的301定向非常简单,那么web.config的301定向又应该怎么实现呢? 先来看下,web.config中的301格式 <?xml version="1.0&qu ...
- ptyhon class定制方法
__iter__ 如果一个类想被用于for ... in循环.须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__() ...
- springMvc返回Json中自定义日期格式
(一)输出json数据 springmvc中使用jackson-mapper-asl即可进行json输出,在配置上有几点: 1.使用mvc:annotation-driven 2.在依赖管理中添加ja ...
- DataTable和Json的相互转换
1 #region DataTable 转换为Json字符串实例方法 2 /// <summary> 3 /// GetClassTypeJosn 的摘要说明 4 /// </sum ...
- MySQL常用API函数
-- 返回数字的绝对值 SELECT ABS(-10) -- 返回不大于X的最大整数值 SELECT FLOOR(10.9) -- 返回不小于X的最大整数值 SELECT CEILING(10.4) ...
- FactoryBean的实现原理与作用
FactoryBean与BeanFactory: 这俩货在拼写上很是相似,很多同学在看IOC源码或者其他地方并不能分清有啥区别,前面的IOC源码中我简单说过,现在统一简单来讲一下: FactoryBe ...
- Spring自定义注解(验证身份证+性别+地区)
第一步:定义注解 PersonFormId: package com.wbg.maven1128.demo_formatter; import java.lang.annotation.*; @Doc ...
- PAT——1007. 素数对猜想
让我们定义 dn 为:dn = pn+1 - pn,其中 pi 是第i个素数.显然有 d1=1 且对于n>1有 dn 是偶数.“素数对猜想”认为“存在无穷多对相邻且差为2的素数”. 现给定任意正 ...
- (CodeForces - 5C)Longest Regular Bracket Sequence(dp+栈)(最长连续括号模板)
(CodeForces - 5C)Longest Regular Bracket Sequence time limit per test:2 seconds memory limit per tes ...
- 陈正冲老师讲c语言之内存的申请malloc() 和释放free()
1.如何使用 malloc 函数 不要莫名其妙,其实上面这段小小的对话,就是malloc的使用过程.malloc是一个函数,专门用来从堆上分配内存.使用malloc函数需要几个要求: 内存分配给谁?分 ...