【JavaScript数据结构系列】03-队列Queue
【JavaScript数据结构系列】03-队列Queue
码路工人 CoderMonkey
转载请注明作者与出处
1. 认识队列Queue结构
队列,跟我们的日常生活非常贴近,我们前面举例了食堂排队打饭的例子,我们继续用这个例子来说明。
如上图所示,
- 第一个加入队列的为队列头
- 最后一个为队列尾
- FIFO:先进先出,后进后出的原则
- 添加删除操作:只能添加到队尾,只能删除队列头
去银行办业务要先取号,然后等待叫号,一样一样的。
(银行也有VIP,后面我们讲优先队列)
2. 队列的应用
- JavaScript事件队列
- 图的广度优先遍历中使用队列
- 打印时的打印任务队列
等。
3. 队列的实现
注:
ES6 版的代码实现请查看 npm 包 data-struct-js 代码
Github/Gitee 上都能找到npm install data-struct-js
3.1 常用方法
与栈的比较类似
方法 | 描述 |
---|---|
enqueue(element) | 添加元素到队列 |
dequeue() | 删除队列元素 |
front() | 查看当前队列头元素 |
isEmpty() | 检查是否为空队列 |
size() | 检查队列容量 |
clear() | 清空队列 |
toString() | 字符串化 |
3.2 常用方法的代码实现
队列也是线性结构,下面我们基于数组来实现一下它的常用方法。
首先,写出 Queue 的构造函数
function Queue() {
this.__items = []
}
3.2.1 enqueue
实现分析:
用push向队列添加元素
function Queue() {
this.__items = []
Queue.prototype.enqueue = function (element) {
return this.__items.push(element)
}
}
3.2.2 dequeue
实现分析:
用shift删除队列排头元素
function Queue() {
this.__items = []
Queue.prototype.dequeue = function () {
return this.__items.shift()
}
}
3.2.3 front
实现分析:
查看队列头即数组第一个元素
function Queue() {
this.__items = []
Queue.prototype.front = function () {
return this.__items.length === 0 ? undefined : this.__items[0]
}
}
3.2.4 isEmpty
实现分析:
只要看内部数组元素个数是否为0
function Queue() {
this.__items = []
Queue.prototype.isEmpty = function () {
return __items.length === 0
}
}
3.2.5 size
实现分析:
返回内部数组元素个数
function Queue() {
this.__items = []
Queue.prototype.size = function () {
return this.__items.length
}
}
3.2.6 clear
实现分析:
只要看元素个数是否为0
function Queue() {
this.__items = []
Queue.prototype.clear = function () {
this.__items.length = 0
}
}
3.2.7 toString
实现分析:
将内部数组用 join 连结
function Queue() {
this.__items = []
Queue.prototype.toString = function() {
return this.__items.join(',')
}
}
完整代码:
function Queue() {
this.__items = []
Queue.prototype.enqueue = function (element) {
this.__items.push(element)
}
Queue.prototype.dequeue = function () {
return this.__items.shift()
}
Queue.prototype.front = function () {
return this.__items.length === 0 ? undefined : this.__items[0]
}
Queue.prototype.isEmpty = function () {
return this.__items.length === 0
}
Queue.prototype.size = function () {
return this.__items.length
}
Stack.prototype.clear = function () {
this.__items.length = 0
}
Queue.prototype.toString = function () {
return this.__items.join(' ')
}
}
3.3 下面我们测试一下
// ---------------------------------------------
// Test: Queue
// ---------------------------------------------
var q = new Queue()
for (var i = 0; i < 5; i++) {
q.enqueue(i)
}
console.log('isEmpty: ', q.isEmpty())
console.log('size: ', q.size())
console.log('toString: ', q.toString())
while (!q.isEmpty()) {
console.log(`dequeue: `, q.dequeue())
}
console.log('isEmpty: ', q.isEmpty())
console.log('size: ', q.size())
// 打印结果:
// isEmpty: false
// size: 5
// toString: 0 1 2 3 4
// dequeue: 0
// dequeue: 1
// dequeue: 2
// dequeue: 3
// dequeue: 4
// isEmpty: true
// size: 0
我们得到了符合预期的结果。
4. 思考题:基于栈结构的队列实现
4.1 栈与队列的顺序原则是相反的,如何用栈实现上面的队列?
学完了 Stack 和 Queue这两个常用的典型数据结构,
让我们暂时放慢攀登的脚步,缓缓消化一下:
这时候正合适练习一下,在使用中加深对这两个数据结构的理解,
发现我们实现的代码中,还要哪些问题,哪些地方可以改善。
在解答之前,让我们先对之前的栈稍做改进。
回想一下,我们实现的栈结构常用方法中,还欠缺什么?
- 不能获取元素集合
- 没有办法遍历集合
4.1.1 对已实现的栈结构的改进
- 添加获取元素集合的方法
Stack.js
function Stack() {
this.__items = []
// ...
Stack.prototype.getItems = function() {
return this.__items
}
}
这样,是实现了,但是,
这里返回 __items 在简单类型时值传递没有问题,
复杂类型时,返回的引用使外部可以对其进行修改,
这就不符合我们栈的要求了。
// 错误结果示例:
var s = new Stack()
s.push(22)
s.push(33)
console.log(s) // => [22, 33]
var items = s.getItems()
items[0] = 11
console.log(s) // => [11, 33]
// ↑ 栈结构的数据不应该被这样修改
- 获取元素集合的改进
这里应该使用深拷贝的,我们来简单实现一下
这样的工具方法积累多了你可以写一个自己的工具函数库了
// 工具方法:深拷贝
function deepCopy(source) {
var dest
if(Array.isArray(source)) {
dest = []
for (let i = 0; i < source.length; i++) {
dest[i] =deepCopy(source[i])
}
}
else if(toString.call(source) === '[object Object]') {
dest = {}
for(var p in source){
if(source.hasOwnProperty(p)){
dest[p]=deepCopy(source[p])
}
}
}
else {
dest = source
}
return dest
}
Stack.js
Stack.prototype.getItems = function() {
this.__items = []
// ...
Stack.prototype.getItems = function() {
return deepCopy(this.__items)
}
}
测试一下:
// 正确结果示例:
var s = new Stack()
s.push(22)
s.push(33)
console.log(s) // => [22, 33]
var items = s.getItems()
items[0] = 11
console.log(s) // => [22, 33]
// ↑ 栈结构的数据未被修改
- 遍历集合
Stack.js
这里遍历时使用上面刚添加的 getItems 方法,在回调中传入集合元素副本
Stack.prototype.traverse = function (cb) {
if (!cb || toString.call(cb) !== '[object Function]') return
var items = this.getItems()
for (var index = items.length - 1; index >= 0; index--) {
var element = items[index];
cb(element, index)
}
}
同理,可以给队列 Queue 添加以上两个方法
(获取集合的getItems方法和遍历的traverse方法),
你可以试试呀
下面我们就去完成基于栈结构的队列实现。
4.2 代码实现
实现分析:
栈与队列的顺序原则刚好是相反的,要用栈实现队列,就需要两个栈,
一个放传入的元素,一个放调整顺序后准备出队的元素。
__inStack
:用于入队__outStack
:用于出队- 入队时:直接入队,
push
进__inStack
- 出队时:出队集合
__outStack
中有元素则直接出队,没有的话将__inStack
中的元素倒过来
function QueueBasedOnStack() {
this.__inStack = new Stack()
this.__outStack = new Stack()
QueueBasedOnStack.prototype.enqueue = function(element) {
this.__inStack.push(element)
}
QueueBasedOnStack.prototype.dequeue = function() {
if(this.__outStack.isEmpty()) {
while(!this.__inStack.isEmpty()) {
this.__outStack.push(this.__inStack.pop())
}
}
return this.__outStack.pop()
}
QueueBasedOnStack.prototype.size = function() {
return (this.__inStack.size() + this.__outStack.size())
}
QueueBasedOnStack.prototype.isEmpty = function() {
return this.__inStack.size() === 0 && this.__outStack.size() === 0
}
QueueBasedOnStack.prototype.clear = function() {
this.__inStack.clear()
this.__outStack.clear()
}
QueueBasedOnStack.prototype.toString = function() {
var items = this.getItems()
return items.join('--')
}
QueueBasedOnStack.prototype.getItems = function() {
return this.__inStack.getItems().concat(this.__outStack.getItems().reverse())
}
}
测试一下
// ---------------------------------------------
// Test: QueueBasedOnStack
// ---------------------------------------------
console.log('----Test: QueueBasedOnStack----')
var qs = new QueueBasedOnStack()
qs.enqueue('A')
console.log('after enqueue: ', qs.toString())
qs.enqueue('B')
console.log('after enqueue: ', qs.toString())
qs.enqueue('C')
qs.enqueue('D')
qs.enqueue('E')
console.log('after enqueue: ', qs.toString())
qs.dequeue()
console.log('after dequeue: ', qs.toString())
qs.dequeue()
console.log('after dequeue: ', qs.toString())
qs.dequeue()
console.log('after dequeue: ', qs.toString())
查看结果:
----Test: QueueBasedOnStack----
after enqueue: A
after enqueue: A--B
after enqueue: A--B--C--D--E
after dequeue: B--C--D--E
after dequeue: C--D--E
after dequeue: D--E
收工。
做了一份 npm 工具包 data-struct-js
,
基于 ES6 实现的 JavaScript 数据结构,
虽然这个小轮子很少会被使用,
也许对于初学者学习 JavaScript 会有点帮助。
只要简单 install 一下即可,感兴趣的话还可以去
GitHub / Gitee 看源码。(Star 表支持~)
npm install data-struct-js --save-dev
https://github.com/CoderMonkie/data-struct-js
https://gitee.com/coder-monkey/data-struct-js
最后,感谢您的阅读和支持~
-end-
【JavaScript数据结构系列】03-队列Queue的更多相关文章
- 【JavaScript数据结构系列】04-优先队列PriorityQueue
[JavaScript数据结构系列]04-优先队列PriorityQueue 码路工人 CoderMonkey 转载请注明作者与出处 ## 1. 认识优先级队列 经典的案例场景: 登机时经济舱的普通队 ...
- 【JavaScript数据结构系列】00-开篇
[JavaScript数据结构系列]00-开篇 码路工人 CoderMonkey 转载请注明作者与出处 ## 0. 开篇[JavaScript数据结构与算法] 大的计划,写以下两部分: 1[JavaS ...
- javascript数据结构与算法---队列
javascript数据结构与算法---队列 队列是一种列表,不同的是队列只能在队尾插入元素,在队首删除元素.队列用于存储按顺序排列的数据,先进先出,这点和栈不一样(后入先出).在栈中,最后入栈的元素 ...
- JavaScript数据结构与算法-队列练习
队列的实现 // 队列类 function Deque () { this.dataStore = []; this.enqueueFront = enqueueFront; this.enqueue ...
- JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象
本篇体验通过硬编码.工厂模式.构造函数来创建JavaScript对象. □ 通过硬编码创建JavaScript对象 当需要创建一个JavaScript对象时,我们可能这样写: var person = ...
- 【JavaScript数据结构系列】07-循环链表CircleLinkedList
[JavaScript数据结构系列]07-循环链表CircleLinkedList 码路工人 CoderMonkey 转载请注明作者与出处 1. 认识循环链表 首节点与尾节点相连的,就构成循环链表.其 ...
- 【JavaScript数据结构系列】05-链表LinkedList
[JavaScript数据结构系列]05-链表LinkedList 码路工人 CoderMonkey 转载请注明作者与出处 ## 1. 认识链表结构(单向链表) 链表也是线性结构, 节点相连构成链表 ...
- 【JavaScript数据结构系列】06-双向链表DoublyLinkedList
[JavaScript数据结构系列]06-双向链表DoublyLinkedList 码路工人 CoderMonkey 转载请注明作者与出处 1. 认识双向链表 不同于普通链表/单向链表,双向链表最突出 ...
- 【JavaScript数据结构系列】02-栈Stack
[JavaScript数据结构系列]02-栈Stack 码路工人 CoderMonkey 转载请注明作者与出处 ## 1. 认识栈结构 栈是非常常用的一种数据结构,与数组同属线性数据结构,不同于数组的 ...
随机推荐
- 图像处理之OpenCV - 缩放/旋转/裁剪/加噪声/高斯模糊
Github地址 @ 缩放 void cv::resize ( InputArray src, OutputArray dst, Size dsize, , , int interpolation = ...
- CF思维联系–CodeForces-217C C. Formurosa(这题鸽了)
ACM思维题训练集合 The Bytelandian Institute for Biological Research (BIBR) is investigating the properties ...
- Android RecyclerView滚动类控件修改、去掉滑动边界的阴影效果
前言 滚动类控件,大家都用的很多,如 RecyclerView.NestedSrollView.... 下面以recyclerView为例讲解,其他滚动控件也同理. RecyclerView 滚动列表 ...
- uniapp中引入less文件
uniapp入门遇到的问题记录 在uniapp中从外部import less文件的话,首先需要在 工具>插件安装 中安装支持less语法的插件,然后在.vue文件中引入 @import url ...
- Tarjan缩点割点(模板)
描述:https://www.luogu.com.cn/problem/P3387 给定一个 nn 个点 mm 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权 ...
- F. Multicolored Markers 暴力+二分
F. Multicolored Markers 题目大意: 给你 a个红块 b个蓝块 拼成一个实心矩形,并且要求红块或者蓝块自成一个矩形,问形成的这个矩形最小的周长是多少. #include < ...
- 掌握这10种方法帮你快速在Linux上分析二进制文件
我们每天都使用二进制文件,但对二进制文件知之甚少.二进制是指您每天运行的可执行文件,从命令行工具到成熟的应用程序.Linux提供了丰富的工具集,可轻松进行二进制分析!无论您的工作角色是什么,如果您在L ...
- 高通Vuforia(Unity3D)云识别初级使用教程
高通Vuforia(Unity3D)云识别初级使用教程 最近因项目开发需要,接触了高通的AR引擎Vuforia云识别,个人感觉稳定性还是很不错的,唯一不爽的地方就是免费的云识别库每个月只能识别10 ...
- 应用视觉设计——CSS实现线性渐变效果
在freeCodeCamp中应用视觉设计的课程中,介绍了一种通过使用CSS属性实现线性渐变效果. 1.线性渐变: background:linear-gredient(gradient-directi ...
- JS 面向对象封装 无限轮播 插件。
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...