JavaScript数据结构与算法---队列和双端队列

队列

定义:

  1. 队列是遵循 先进先出 原则的一种有序的项
  2. 队列在尾部添加新的元素,并从顶部移除元素
  3. 最新添加的元素必须排在队列的末尾
  4. 队列最通俗的讲就是排队,生活中的排队,先排的人先去处理服务

队列的实现:

  1. 创建队列
// count属性控制队列的大小
// lowestCount属性追踪第一个元素
class Queue {
    constructor() {
        this.count = 0
        this.lowestCount = 0
        this.items = {}
    }
}
  1. 向队列添加元素
// 新的项只能添加到队列的末尾(和栈的实现相似,将count作为items的键)
enqueue(element) {
    this.items[this.count] = element
    this.count++
}
  1. 判断队列是否为空
isEmpty() {
    return this.count - this.lowestCount === 0
}
  1. 从队列移除元素
// 首先检查队列是否为空,是空则返回undefined。否则将队列头部的值先暂存起来,以便该元素被移除后将它返回
dequeue() {
    if (this.isEmpty()) {
        return undefined
    }
    const result = this.items[this.lowestCount]
    delete this.items[this.lowestCount]
    this.lowestCount++
    return result
}

  1. 查看队列的头元素
// 返回队列最前面的项
peek() {
    if (this.isEmpty()) {
        return undefined
    }
    return this.items[this.lowestCount]
}
  1. 查看队列大小
size() { 
		return this.count - this.lowestCount
}
  1. 清空队列
clear() { 
		this.items = {}
		this.count = 0
		this.lowestCount = 0
}
  1. toString()
toString() { 
		if (this.isEmpty()) { 
 				return ''
 		} 
 		let objString = `${this.items[this.lowestCount]}`
 		for (let i = this.lowestCount + 1; i < this.count; i++) { 
 				objString = `${objString},${this.items[i]}`
		} 
		return objString
}
  1. 使用Queue类
const quene = new Queue()
console.log(quene.isEmpty())
quene.enqueue('苹果')
quene.enqueue('香蕉')
quene.enqueue('葡萄')
console.log(quene)
console.log(quene.toString())
quene.dequeue()
quene.dequeue()
console.log(quene)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3mn8LdVO-1620461139480)(/Users/yinlu/Documents/截屏/截屏2021-05-08 下午1.48.05.png)]

双端队列

定义:

  1. 是一种同时允许我们从前端后端添加和移除元素的特殊队列

双端队列的实现

  1. 创建双端队列
class Deque {
    constructor() {
        this.count = 0
        this.lowestCount = 0
        this.items = {}
    }
}
  1. 向双端队列的后端添加元素
addBack(element) {
    this.items[this.count] = element
    this.count++
}
  1. 向双端队列的前端添加元素

向双端队列的前端添加元素存在三种场景
第一种是这个双端队列是空的,执行addBack()方法,元素会被添加到双端队列的后端,当然,在这里也是双端队列的前端

第二种场景是一个元素已经被从双端队列的前端移除,也就是说lowestCount属性会大于等于1,这种情况下,要将lowestCount属性减1并将新元素的值放在这个键的位置上即可

第三种也是最后一种场景是 lowestCount 为 0 的情况。我们可以设置一个负值的键,同时更新用于计算双端队列长度的逻辑,使其也能包含负键值。这种情况下,添加一个新元素的操作仍然能保持最低的计算成本。为了便于演示,我们把本场景看作使用数组。要在第一位添加一个新元素,我们需要将所有元素后移一位来空出第一个位置。由于我们不想丢失任何已有的值,需要从最后一位开始迭代所有的值,并为元素赋上索引值减 1 位置的值。在所有的元素都完成移动后,第一位将是空闲状态,这样就可以用需要添加的新元素来覆盖它了

addFront(element) {
    if (this.isEmpty()) {
        this.addBack(element)
    } else if (this.lowestCount > 0) {
        this.lowestCount--
        this.items[this.lowestCount] = element
    } else {
        for (let i = this.count; i > 0; i--) {
            this.items[i] = this.items[i - 1]
        }
        this.count++
        this.lowestCount = 0
        this.items[0] = element
    }
}
  1. 从双端队列的前后端移除元素
// 从双端队列前端移除元素
removeFront() {
    if (this.isEmpty()) {
        return undefined
    }
    const result = this.items[this.lowestCount]
    delete this.items[this.lowestCount]
    this.lowestCount++
    return result
}

// 从双端队列后端移除元素
removeBack() {
    if (this.isEmpty()) {
        return undefined
    }
    this.count--
    const result = this.items[this.count]
    delete this.items[this.count]
    return result
}
  1. 返回双端队列的前后端第一个元素
// 返回双端队列前端的第一个元素
peekFront() {
    if (this.isEmpty()) {
        return undefined
    }
    return this.items[this.lowestCount]
}

// 返回双端队列后端的第一个元素
peekBack() {
    if (this.isEmpty()) {
        return undefined
    }
    return this.items[this.count - 1]
}
  1. 使用Deque类
const deque = new Deque()
console.log(deque.isEmpty()) // 输出 true 
deque.addBack('John')
deque.addBack('Jack')
deque.addBack('Camila')
console.log(deque)
deque.removeFront() // 移除 John 
console.log(deque) // Jack, Camila 
deque.removeBack() // Camila 决定离开
console.log(deque) // Jack 
deque.addFront('John') // John 回来询问一些信息
console.log(deque)
console.log(deque.peekFront())
console.log(deque.peekBack())

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKJcou6i-1620461139483)(/Users/yinlu/Documents/截屏/截屏2021-05-08 下午3.01.22.png)]

队列的应用

循环队列-击鼓传花

在这个游戏中,孩子们围成一个圆圈,把花尽快地传递给旁边的人。某一时刻传花停止,

这个时候花在谁手里,谁就退出圆圈、结束游戏。重复这个过程,直到只剩一个孩子(胜者)

// 首先要把参与者的名字都加入队列{2} 
// 然后对队列进行遍历,就是从队列的开头移除一项,然后加在队列的末尾 {3}
// 一旦达到给定的次数,拿着花的那个人就被淘汰,从队列中移除 {4}
// 最后只剩下一个人的时候,这个人就是胜者
function hotPotato(elementsList, num) {
    const queue = new Queue() // {1} 
    const elimitatedList = []
    for (let i = 0; i < elementsList.length; i++) {
        queue.enqueue(elementsList[i]) // {2} 
    }
    while (queue.size() > 1) {
        for (let i = 0; i < num; i++) {
            queue.enqueue(queue.dequeue()) // {3} 
        }
        elimitatedList.push(queue.dequeue()) // {4} 
    }
    return {
        eliminated: elimitatedList,
        winner: queue.dequeue() // {5} 
    }
}
const names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl']
const result = hotPotato(names, 7)
result.eliminated.forEach(name => {
    console.log(`${name}在击鼓传花游戏中被淘汰。`)
})
console.log(`胜利者: ${result.winner}`)

在这里插入图片描述
在这里插入图片描述

来自于《学习JavaScript数据结构与算法》第三版

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y shǔ shǔ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值