【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 的构造函数

  1. function Queue() {
  2. this.__items = []
  3. }

3.2.1 enqueue

实现分析:

用push向队列添加元素

  1. function Queue() {
  2. this.__items = []
  3. Queue.prototype.enqueue = function (element) {
  4. return this.__items.push(element)
  5. }
  6. }

3.2.2 dequeue

实现分析:

用shift删除队列排头元素

  1. function Queue() {
  2. this.__items = []
  3. Queue.prototype.dequeue = function () {
  4. return this.__items.shift()
  5. }
  6. }

3.2.3 front

实现分析:

查看队列头即数组第一个元素

  1. function Queue() {
  2. this.__items = []
  3. Queue.prototype.front = function () {
  4. return this.__items.length === 0 ? undefined : this.__items[0]
  5. }
  6. }

3.2.4 isEmpty

实现分析:

只要看内部数组元素个数是否为0

  1. function Queue() {
  2. this.__items = []
  3. Queue.prototype.isEmpty = function () {
  4. return __items.length === 0
  5. }
  6. }

3.2.5 size

实现分析:

返回内部数组元素个数

  1. function Queue() {
  2. this.__items = []
  3. Queue.prototype.size = function () {
  4. return this.__items.length
  5. }
  6. }

3.2.6 clear

实现分析:

只要看元素个数是否为0

  1. function Queue() {
  2. this.__items = []
  3. Queue.prototype.clear = function () {
  4. this.__items.length = 0
  5. }
  6. }

3.2.7 toString

实现分析:

将内部数组用 join 连结

  1. function Queue() {
  2. this.__items = []
  3. Queue.prototype.toString = function() {
  4. return this.__items.join(',')
  5. }
  6. }

完整代码:

  1. function Queue() {
  2. this.__items = []
  3. Queue.prototype.enqueue = function (element) {
  4. this.__items.push(element)
  5. }
  6. Queue.prototype.dequeue = function () {
  7. return this.__items.shift()
  8. }
  9. Queue.prototype.front = function () {
  10. return this.__items.length === 0 ? undefined : this.__items[0]
  11. }
  12. Queue.prototype.isEmpty = function () {
  13. return this.__items.length === 0
  14. }
  15. Queue.prototype.size = function () {
  16. return this.__items.length
  17. }
  18. Stack.prototype.clear = function () {
  19. this.__items.length = 0
  20. }
  21. Queue.prototype.toString = function () {
  22. return this.__items.join(' ')
  23. }
  24. }

3.3 下面我们测试一下

  1. // ---------------------------------------------
  2. // Test: Queue
  3. // ---------------------------------------------
  4. var q = new Queue()
  5. for (var i = 0; i < 5; i++) {
  6. q.enqueue(i)
  7. }
  8. console.log('isEmpty: ', q.isEmpty())
  9. console.log('size: ', q.size())
  10. console.log('toString: ', q.toString())
  11. while (!q.isEmpty()) {
  12. console.log(`dequeue: `, q.dequeue())
  13. }
  14. console.log('isEmpty: ', q.isEmpty())
  15. console.log('size: ', q.size())
  16. // 打印结果:
  17. // isEmpty: false
  18. // size: 5
  19. // toString: 0 1 2 3 4
  20. // dequeue: 0
  21. // dequeue: 1
  22. // dequeue: 2
  23. // dequeue: 3
  24. // dequeue: 4
  25. // isEmpty: true
  26. // size: 0

我们得到了符合预期的结果。

4. 思考题:基于栈结构的队列实现

4.1 栈与队列的顺序原则是相反的,如何用栈实现上面的队列?

学完了 Stack 和 Queue这两个常用的典型数据结构,
让我们暂时放慢攀登的脚步,缓缓消化一下:
这时候正合适练习一下,在使用中加深对这两个数据结构的理解,
发现我们实现的代码中,还要哪些问题,哪些地方可以改善。

在解答之前,让我们先对之前的栈稍做改进。

回想一下,我们实现的栈结构常用方法中,还欠缺什么?

  • 不能获取元素集合
  • 没有办法遍历集合

4.1.1 对已实现的栈结构的改进

  • 添加获取元素集合的方法

Stack.js

  1. function Stack() {
  2. this.__items = []
  3. // ...
  4. Stack.prototype.getItems = function() {
  5. return this.__items
  6. }
  7. }

这样,是实现了,但是,

这里返回 __items 在简单类型时值传递没有问题,

复杂类型时,返回的引用使外部可以对其进行修改,

这就不符合我们栈的要求了。

  1. // 错误结果示例:
  2. var s = new Stack()
  3. s.push(22)
  4. s.push(33)
  5. console.log(s) // => [22, 33]
  6. var items = s.getItems()
  7. items[0] = 11
  8. console.log(s) // => [11, 33]
  9. // ↑ 栈结构的数据不应该被这样修改
  • 获取元素集合的改进
    这里应该使用深拷贝的,我们来简单实现一下
    这样的工具方法积累多了你可以写一个自己的工具函数库了
  1. // 工具方法:深拷贝
  2. function deepCopy(source) {
  3. var dest
  4. if(Array.isArray(source)) {
  5. dest = []
  6. for (let i = 0; i < source.length; i++) {
  7. dest[i] =deepCopy(source[i])
  8. }
  9. }
  10. else if(toString.call(source) === '[object Object]') {
  11. dest = {}
  12. for(var p in source){
  13. if(source.hasOwnProperty(p)){
  14. dest[p]=deepCopy(source[p])
  15. }
  16. }
  17. }
  18. else {
  19. dest = source
  20. }
  21. return dest
  22. }

Stack.js

  1. Stack.prototype.getItems = function() {
  2. this.__items = []
  3. // ...
  4. Stack.prototype.getItems = function() {
  5. return deepCopy(this.__items)
  6. }
  7. }

测试一下:

  1. // 正确结果示例:
  2. var s = new Stack()
  3. s.push(22)
  4. s.push(33)
  5. console.log(s) // => [22, 33]
  6. var items = s.getItems()
  7. items[0] = 11
  8. console.log(s) // => [22, 33]
  9. // ↑ 栈结构的数据未被修改
  • 遍历集合

Stack.js

这里遍历时使用上面刚添加的 getItems 方法,在回调中传入集合元素副本

  1. Stack.prototype.traverse = function (cb) {
  2. if (!cb || toString.call(cb) !== '[object Function]') return
  3. var items = this.getItems()
  4. for (var index = items.length - 1; index >= 0; index--) {
  5. var element = items[index];
  6. cb(element, index)
  7. }
  8. }

同理,可以给队列 Queue 添加以上两个方法

(获取集合的getItems方法和遍历的traverse方法),

你可以试试呀

下面我们就去完成基于栈结构的队列实现。

4.2 代码实现

实现分析:

栈与队列的顺序原则刚好是相反的,要用栈实现队列,就需要两个栈,

一个放传入的元素,一个放调整顺序后准备出队的元素。

  • __inStack :用于入队
  • __outStack :用于出队
  • 入队时:直接入队, push 进 __inStack
  • 出队时:出队集合__outStack 中有元素则直接出队,没有的话将 __inStack 中的元素倒过来
  1. function QueueBasedOnStack() {
  2. this.__inStack = new Stack()
  3. this.__outStack = new Stack()
  4. QueueBasedOnStack.prototype.enqueue = function(element) {
  5. this.__inStack.push(element)
  6. }
  7. QueueBasedOnStack.prototype.dequeue = function() {
  8. if(this.__outStack.isEmpty()) {
  9. while(!this.__inStack.isEmpty()) {
  10. this.__outStack.push(this.__inStack.pop())
  11. }
  12. }
  13. return this.__outStack.pop()
  14. }
  15. QueueBasedOnStack.prototype.size = function() {
  16. return (this.__inStack.size() + this.__outStack.size())
  17. }
  18. QueueBasedOnStack.prototype.isEmpty = function() {
  19. return this.__inStack.size() === 0 && this.__outStack.size() === 0
  20. }
  21. QueueBasedOnStack.prototype.clear = function() {
  22. this.__inStack.clear()
  23. this.__outStack.clear()
  24. }
  25. QueueBasedOnStack.prototype.toString = function() {
  26. var items = this.getItems()
  27. return items.join('--')
  28. }
  29. QueueBasedOnStack.prototype.getItems = function() {
  30. return this.__inStack.getItems().concat(this.__outStack.getItems().reverse())
  31. }
  32. }

测试一下

  1. // ---------------------------------------------
  2. // Test: QueueBasedOnStack
  3. // ---------------------------------------------
  4. console.log('----Test: QueueBasedOnStack----')
  5. var qs = new QueueBasedOnStack()
  6. qs.enqueue('A')
  7. console.log('after enqueue: ', qs.toString())
  8. qs.enqueue('B')
  9. console.log('after enqueue: ', qs.toString())
  10. qs.enqueue('C')
  11. qs.enqueue('D')
  12. qs.enqueue('E')
  13. console.log('after enqueue: ', qs.toString())
  14. qs.dequeue()
  15. console.log('after dequeue: ', qs.toString())
  16. qs.dequeue()
  17. console.log('after dequeue: ', qs.toString())
  18. qs.dequeue()
  19. console.log('after dequeue: ', qs.toString())

查看结果:

  1. ----Test: QueueBasedOnStack----
  2. after enqueue: A
  3. after enqueue: A--B
  4. after enqueue: A--B--C--D--E
  5. after dequeue: B--C--D--E
  6. after dequeue: C--D--E
  7. after dequeue: D--E

收工。


做了一份 npm 工具包 data-struct-js
基于 ES6 实现的 JavaScript 数据结构,
虽然这个小轮子很少会被使用,
也许对于初学者学习 JavaScript 会有点帮助。
只要简单 install 一下即可,感兴趣的话还可以去
GitHub / Gitee 看源码。(Star 表支持~)

  1. 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的更多相关文章

  1. 【JavaScript数据结构系列】04-优先队列PriorityQueue

    [JavaScript数据结构系列]04-优先队列PriorityQueue 码路工人 CoderMonkey 转载请注明作者与出处 ## 1. 认识优先级队列 经典的案例场景: 登机时经济舱的普通队 ...

  2. 【JavaScript数据结构系列】00-开篇

    [JavaScript数据结构系列]00-开篇 码路工人 CoderMonkey 转载请注明作者与出处 ## 0. 开篇[JavaScript数据结构与算法] 大的计划,写以下两部分: 1[JavaS ...

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

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

  4. JavaScript数据结构与算法-队列练习

    队列的实现 // 队列类 function Deque () { this.dataStore = []; this.enqueueFront = enqueueFront; this.enqueue ...

  5. JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象

    本篇体验通过硬编码.工厂模式.构造函数来创建JavaScript对象. □ 通过硬编码创建JavaScript对象 当需要创建一个JavaScript对象时,我们可能这样写: var person = ...

  6. 【JavaScript数据结构系列】07-循环链表CircleLinkedList

    [JavaScript数据结构系列]07-循环链表CircleLinkedList 码路工人 CoderMonkey 转载请注明作者与出处 1. 认识循环链表 首节点与尾节点相连的,就构成循环链表.其 ...

  7. 【JavaScript数据结构系列】05-链表LinkedList

    [JavaScript数据结构系列]05-链表LinkedList 码路工人 CoderMonkey 转载请注明作者与出处 ## 1. 认识链表结构(单向链表) 链表也是线性结构, 节点相连构成链表 ...

  8. 【JavaScript数据结构系列】06-双向链表DoublyLinkedList

    [JavaScript数据结构系列]06-双向链表DoublyLinkedList 码路工人 CoderMonkey 转载请注明作者与出处 1. 认识双向链表 不同于普通链表/单向链表,双向链表最突出 ...

  9. 【JavaScript数据结构系列】02-栈Stack

    [JavaScript数据结构系列]02-栈Stack 码路工人 CoderMonkey 转载请注明作者与出处 ## 1. 认识栈结构 栈是非常常用的一种数据结构,与数组同属线性数据结构,不同于数组的 ...

随机推荐

  1. Process Synchronization-Example 1

    问题描述 把学生和监考老师都看作进程,学生有N人,教师1人.考场门口每次只能进出一个人,进考场原则是先来先进.当N个学生都进入考场后,教师才能发卷子.学生交卷后可以离开考场,教师要等收上来全部卷子并封 ...

  2. CF思维联系–CodeForces-217C C. Formurosa(这题鸽了)

    ACM思维题训练集合 The Bytelandian Institute for Biological Research (BIBR) is investigating the properties ...

  3. USACO Training Section 1.1 Your Ride Is Here

    题目描述 众所周知,在每一个彗星后都有一只UFO.这些UFO时常来收集地球上的忠诚支持者.不幸的是,他们的飞碟每次出行都只能带上一组支持者.因此,他们要用一种聪明的方案让这些小组提前知道谁会被彗星带走 ...

  4. POJ 1330 Nearest Common Ancestors(裸LCA)

    Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 39596   Accept ...

  5. python(os 模块)

    1.os.name 输出字符串指示正在使用的平台.如果是window 则用'nt'表示,对于Linux/Unix用户,它是'posix' import os print(os.name) #结果如下 ...

  6. java基础篇 之 super关键字的理解

    ​ 之前一直认为,super指向的是父类对象.到今天,仔细查询了资料,自己做了实验,确认这个结论是不对的.我们分一下几个点讨论下: super的作用: 第一种:用来访问父类被隐藏的成员变量 第二种:用 ...

  7. apply call bind的用法与实现

    概念 apply call 和bind 允许为不同的对象分配和调用属于一个对象的函数/方法.同时它们可以改变函数内 this 的指向. 区别 apply 和 call 接收的参数形式不同 apply ...

  8. [hdu4513]常规dp

    题意:给一个长度为m的序列,从里面选出一些数,相对位置不发生变化,并满足a[i]=a[n-i],a[1]<a[2]<...<a[(n+1)/2],n是数的个数,求最大的n 思路:dp ...

  9. [zoj3627]模拟吧

    思路:情况只可能是2种,两个人一直向一边走,或者有一个人折回来,对于后一种,枚举折回来的位置就行了.不过要注意两个方向都要处理下. #pragma comment(linker, "/STA ...

  10. Java面试札记

    Java面试札记  在最深的夜里,即使是你的影子也会离你而去. 背景:愿某人在中秋节之前吃上大厂月饼!!!@CDZ 1.Java的八种基本数据类型? 整型:byte.int.short.long: 浮 ...