定场诗

马瘦毛长蹄子肥,儿子偷爹不算贼,瞎大爷娶个瞎大奶奶,老两口过了多半辈,谁也没看见谁!

前言

本章为重读《学习JavaScript数据结构与算法-第三版》的系列文章,主要讲述队列数据结构、双端队列数据结构以及队列相关应用。

队列

队列是遵循先进先出(FIFO)原则的一组有序的项。队列在尾部添加元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。现实中常见的队列就是排队,计算机科学中,常见的例子是打印队列,如文档按顺序打印,第一个发送到打印队列的文档优先被打印。

实现队列

/**
* class Queue 队列类
* 特点:先进先出
*/
class Queue {
construcor () {
// 存储数据
this.items = {}
// 队列头部元素索引
this.lowestCount = 0
// 队列尾部元素索引
this.count = 0
} /**
* enqueue() 添加元素到队列 - 先进先出
* @param {*} element 添加到队列的元素
*/
enqueue (elemenet) {
this.items[this.count] = element
this.count++
} /**
* dequeue() 移除队列头部元素 - 先进先出
* @returns {*} result 返回头部元素
*/
dequeue () {
if (this.isEmpty()) {
return undefined
}
let result = this.items[this.lowestCount]
delete this.items[this.lowestCount]
this.lowestCount++
return result
} /**
* peek() 返回队列头部元素
* @returns {*}
*/
peek () {
if (this.isEmpty()) {
return undefined
}
return this.items[this.lowestCount]
} /**
* isEmpty() 判断队列是否为空
* @returns {Boolean}
*/
isEmpty () {
return this.count === this.lowestCount
} /**
* size() 队列长度
* @returns {Number}
*/
size () {
return this.count - this.lowestCount
} /**
* clear() 清空队列
*/
clear () {
this.items = {}
this.count = 0
this.lowestCount = 0
} /**
* toString() 返回队列的字符串结构
* @returns {String}
*/
toString () {
if (this.isEmpty()) {
return ''
}
let queueStr = `${this.items[this.lowestCount]}`
for (let i = this.lowestCount + 1; i < this.count; i++) {
queueStr = `${queueStr},${this.items[i]}`
}
return queueStr
}
}

使用队列

// 实例化队列
const queue = new Queue() // 判断队列是否为空
console.log(queue.isEmpty()) // true // 向队列中添加元素
queue.enqueue('John')
queue.enqueue('Jack') console.log(queue.toString()) // John,Jack queue.enqueue('Camila') console.log(queue.toString()) // John,Jack console.log(queue.size()) // 3 console.log(queue.isEmpty()) // false

以上操作示意图:

console.log(queue.dequeue()) // John
console.log(queue.dequeue()) // Jack
console.log(queue.toString()) // Camila

以上操作示意图

双端队列

双端队列是一种允许我们同时从前端和后端添加和移除元素的特殊队列。在计算机科学中双端队列常见应用是存储一系列的撤销操作。

  1. 当用户在软件中进行了操作时,该操作从尾部进入双端队列;
  2. 当用户点击撤销按钮时,从双端队列的尾部移除;
  3. 当队列中的操作达到预定义的一定数量后,最先存入的操作会被移除(头部移除)

双端队列同时遵守了先进先出和后进先出的原则

实现双端队列

/**
* class Deque 双端队列
* 特点:支持先进先出、后进先出
*/
class Deque {
constructor () {
this.items = {}
this.count = 0
this.lowestCount = 0
} /**
* addFront() 从头部插入
* @param {*} element 待插入队首的元素
*/
addFront (element) {
this.lowestCount--
this.items[this.lowestCount] = element
} /**
* addBack() 从尾部插入
* @param {*} element 待插入队尾的元素
*/
addBack (element) {
this.items[this.count] = element
this.count++
} /**
* removeFront() 队首移除元素
* @returns {*}
*/
removeFront () {
if (this.isEmpty()) {
return undefined
}
let result = this.items[this.lowestCount]
delete this.items[this.lowestCount]
this.lowestCount++
return result
} /**
* removeBack() 队尾移除元素
* @returns {*}
*/
removeBack () {
if (this.isEmpty()) {
return undefined
}
let result = this.items[this.count -1]
delete this.items[this.count - 1]
this.count--
return result
} /**
* isEmpty() 判断是否为空
* @returns {Boolean} 是否为空
*/
isEmpty () {
return this.count === this.lowestCount
} /**
* size() 返回队列的长度
* @returns {Number} 队列的长度
*/
size () {
return this.count - this.lowestCount
} /**
* clear() 清空队列
*/
clear () {
this.items = {}
this.count = 0
this.lowestCount = 0
} /**
* peekFront() 获取队首元素
* @returns {*}
*/
peekFront () {
if (this.isEmpty()) {
return undefined
}
return this.items[this.lowestCount]
} /**
* peekBack() 获取队尾元素
* @returns {*}
*/
peekBack () {
if (this.isEmpty()) {
return undefined
}
return this.items[this.count - 1]
} /**
* toString() 展示字符串形式
* @returns {*}
*/
toString () {
if (this.isEmpty()) {
return ''
}
let str = `${this.items[this.lowestCount]}`
for (let i = this.lowestCount + 1; i < this.count; i++) {
str = `${str},${this.items[i]}`
}
return str
}
}

使用双端队列

const deque = new Deque()

console.log(deque.isEmpty()) // true

deque.addFront('宋江')
deque.addFront('晁盖')
deque.addBack('吴用') console.log(deque.size()) // 3
console.log(deque.toString()) // 晁盖,宋江,吴用 console.log(deque.removeFront()) // 晁盖
console.log(deque.removeBack()) // 吴用 console.log(deque.toString()) // 宋江
console.log(deque.size()) // 1

实战应用

循环队列 - 模拟击鼓传花游戏

规则:

  1. 使用队列模拟击鼓传花
  2. 当执行一定的约定次数(每次都随机一个数)时,花在谁手里,谁被淘汰
  3. 重复执行游戏,直到剩下最后一人,为胜利者
/**
* hotPotato() 模拟击鼓传花游戏
* @param {Array} elementList 需传入的参与游戏的人员列表
* @returns {Object} 返回信息:淘汰者和胜利者
*/
function hotPotato (elementList) {
// 实例化队列,将元素放入队列
let queue = new Queue()
elementList.forEach((value) => {
queue.enqueue(value)
}) // 淘汰的人
let elimitatedList = [] // 开始游戏,直到队列中剩余1人
while (queue.size() > 1) {
// 为了让游戏更加有意思,每次停止的位置进行随机处理
for (let i = 0; i < Math.floor(Math.random() * 10); i++) {
// 取出头部第一个元素,然后执行插入
queue.enqueue(queue.dequeue())
}
// 此时拥有花的人,淘汰
elimitatedList.push(queue.dequeue())
} // 返回
return {
elimitatedList,
winner: queue.dequeue()
}
} let elementList = [
'孙悟空',
'唐僧',
'如来佛祖',
'玉帝',
'王母'
] // 调用函数,传入elementList,同时约定第6次时,拥有花的小伙伴被淘汰...
let result = hotPotato(elementList, 6)
console.log(result) result.elimitatedList.forEach((name) => {
console.log(`${name}被淘汰了...`)
}) console.log(`${result.winner}胜利了...`)

以上代码运行效果 -- 展示其中一次的执行结果

{ elimitatedList: [ '唐僧', '王母', '孙悟空', '如来佛祖' ], winner: '玉帝' }

唐僧被淘汰了...

王母被淘汰了...

孙悟空被淘汰了...

如来佛祖被淘汰了...

玉帝胜利了...

回文检查器

什么是回文?

回文是正反都能读通的单词、词组、数或一系列字符的序列,例如:aba、madam

那如何实现回文检查器呢?

1. 使用反转排列字符串与原字符串进行比较

/**
* palindromeChecker() 回文检查器
* @param {String} str 待检查的字符串
* @returns {Boolean}
*/
function palindromeChecker (str) {
// 检测是否是合法字符串
if (str === undefined || str === null || (str !== null && str.length === 0)) {
return false
}
return str.split('').reverse().join('') === str
} let str = 'aba'
console.log(palindromeChecker(str)) // true

2. 使用数据结构-栈实现回文检查器

/**
* palindromeChecker() 回文检查器
* @param {String} str 待检查的字符串
* @returns {Boolean}
*/
function palindromeChecker (str) {
// 检测是否是合法字符串
if (str === undefined || str === null || (str !== null && str.length === 0)) {
return false
}
let stack = new Stack()
// 分割字符串
for (let i = 0; i < str.length; i++) {
stack.push(str[i])
} let reverseStr = ''
while (!stack.isEmpty()) {
reverseStr += stack.pop()
}
return str === reverseStr
} let str = 'aba'
console.log(palindromeChecker(str)) // true

此处栈 class Stack的实现,请参考上一篇文章:重读《学习JavaScript数据结构与算法-第三版》- 第4章 栈

3. 使用双端队列实现回文检查器

/**
* palindromeChecker() 回文检查器
* @param {String} str 待检查的字符串
* @returns {Boolean}
*/
function palindromeChecker (str) {
// 检测是否是合法字符串
if (str === undefined || str === null || (str !== null && str.length === 0)) {
return false
} let deque = new Deque() for (let i = 0; i < str.length; i++) {
deque.addBack(str[i])
} // 设置是否是回文的标记
let isEqual = true
while (deque.size() > 1 && isEqual) {
// 取出收尾进行比较
let front = deque.peekFront()
let back = deque.peekBack() if (front !== back) {
isEqual = false
}
} return isEqual
} let str = 'abc'
console.log(palindromeChecker(str)) // false
str = 'aba'
console.log(palindromeChecker(str)) // true

后记

以上就是胡哥今天给大家分享的内容,喜欢的小伙伴记得收藏转发、点击右下角按钮在看,推荐给更多小伙伴呦,欢迎多多留言交流...

胡哥有话说,一个有技术,有情怀的胡哥!京东开放平台首席前端攻城狮。与你一起聊聊大前端,分享前端系统架构,框架实现原理,最新最高效的技术实践!

长按扫码关注,更帅更漂亮呦!关注胡哥有话说公众号,可与胡哥继续深入交流呦!

重读《学习JavaScript数据结构与算法-第三版》- 第5章 队列的更多相关文章

  1. 重读《学习JavaScript数据结构与算法-第三版》- 第4章 栈

    定场诗 金山竹影几千秋,云索高飞水自流: 万里长江飘玉带,一轮银月滚金球. 远自湖北三千里,近到江南十六州: 美景一时观不透,天缘有分画中游. 前言 本章是重读<学习JavaScript数据结构 ...

  2. 重读《学习JavaScript数据结构与算法-第三版》-第2章 ECMAScript与TypeScript概述

    定场诗 八月中秋白露,路上行人凄凉: 小桥流水桂花香,日夜千思万想. 心中不得宁静,清早览罢文章, 十年寒苦在书房,方显才高志广. 前言 洛伊安妮·格罗纳女士所著的<学习JavaScript数据 ...

  3. 重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)

    定场诗 伤情最是晚凉天,憔悴厮人不堪言: 邀酒摧肠三杯醉.寻香惊梦五更寒. 钗头凤斜卿有泪,荼蘼花了我无缘: 小楼寂寞新雨月.也难如钩也难圆. 前言 本章为重读<学习JavaScript数据结构 ...

  4. 重读《学习JavaScript数据结构与算法-第三版》- 第3章 数组(一)

    定场诗 大将生来胆气豪,腰横秋水雁翎刀. 风吹鼍鼓山河动,电闪旌旗日月高. 天上麒麟原有种,穴中蝼蚁岂能逃. 太平待诏归来日,朕与先生解战袍. 此处应该有掌声... 前言 读<学习JavaScr ...

  5. 学习JavaScript数据结构与算法 (二)

    学习JavaScript数据结构与算法 的笔记 包含第四章队列, 第五章链表 本人所有文章首发在博客园: http://www.cnblogs.com/zhangrunhao/ 04队列 实现基本队列 ...

  6. 学习JavaScript数据结构与算法 (一)

    学习JavaScript数据结构与算法 的笔记, 包含一二三章 01基础 循环 斐波那契数列 var fibonaci = [1,1] for (var i = 2; i< 20;i++) { ...

  7. 学习JavaScript数据结构与算法---前端进阶系列

    学习建议 1.视频学习---认知 建议:在中国慕课上找"数据结构"相关的视频教程.中国大学MOOC 推荐清华大学.北京大学.浙江大学的教程,可先试看,然后根据自身的情况选择视频进行 ...

  8. 学习Javascript数据结构与算法(第2版)笔记(1)

    第 1 章 JavaScript简介 使用 Node.js 搭建 Web 服务器 npm install http-server -g http-server JavaScript 的类型有数字.字符 ...

  9. 学习JavaScript数据结构与算法 2/15

    第一章 JavaScript简介 js不同于C/C++,C#,JAVA,不是强类型语言. 通常,代码质量可以用全局变量和函数的数量来考量(数量越多越糟).因此,尽可能避免使用全局变量. JS数据类型 ...

随机推荐

  1. 20141111-WCF获得Web路径

    在WCF中调用Server.MapPath 获取服务发布目录路径 在WCF中调用Server.MapPath 获取服务发布目录路径 在WCF中想使用Server.Map获取当前服务发布目录的绝对路径. ...

  2. tmux终端复用神器简单使用

    创建命名Tmux会话(tmux new -s session_name)tmux new -s session_name chongchong 暂退Tmux会话(Ctrl + a d)直接关窗口 返回 ...

  3. Java开发实例大全:3月14日练习

    import java.io.FileNotFoundException; import java.io.PrintStream; import java.math.BigDecimal; impor ...

  4. 洛谷P2172 [国家集训队]部落战争 题解

    题目链接:https://www.luogu.org/problemnew/show/P2172 分析: 不要被[国家集训队]的标签吓到,其实这题不是很难. 本题可以对比P4304 [TJOI2013 ...

  5. C#3.0新增功能09 LINQ 标准查询运算符 04 运算

    连载目录    [已更新最新开发文章,点击查看详细] 本篇主要介绍标准查询运算符的常用运算功能. 01 对数据排序 排序操作基于一个或多个属性对序列的元素进行排序. 第一个排序条件对元素执行主要排序. ...

  6. PTA L2-031 深入虎穴 非dfs的一点想法

    著名的王牌间谍 007 需要执行一次任务,获取敌方的机密情报.已知情报藏在一个地下迷宫里,迷宫只有一个入口,里面有很多条通路,每条路通向一扇门.每一扇门背后或者是一个房间,或者又有很多条路,同样是每条 ...

  7. C#命名规范(简述)

    命名空间,类,事件,接口,常量,属性,方法使用Pascal命名,即首字母大写  参数,变量(类字段)使用camel命名法,即首字母小写. Pascal 方式--所有单词第一个字母大写,其他字母小写.  ...

  8. windows和linux下如何对拍

    对拍是各种计算机考试检查时必备工具,实际上十分强大,只要你的暴力没有写错就没有问题. 对拍的意思:(怎么有点语文课的意思雾) 对:看见'对'就可以知道有两个. 拍:就是把两个程序结果拍在一起,对照(有 ...

  9. 如何使用 nvm-windows 管理 nodejs 版本

    写在前边的话: (1). 路径一定不要包含空格,如 Program Files 这样,所以有把程序安装到 D:\Program Files 文件下的同学请注意了:(2). 为了避免 nvm 无法切换源 ...

  10. 技术派-不用sqrt手工计算平方根

    题目:任意长度数串,不使用sqrt函数,手工计算平方根?   要求只准用加/减/乘/除四则运算,不准使用power/sqrt等函数.   算法如下: 1.以小数点为中心往两边每2位分隔为一组: 2.然 ...