五,队列和双端队列
我们已经学习了栈。队列和栈非常类似,但是使用了与后进先出不同的原则。
双端队列是一种将栈的原则和队列的原则混合在一起的数据结构。
5.1 队列数据结构
队列是遵循先进先出(FIFO,也称为先来先服务)原则的一组有序的项。队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。
在计算机科学中,一个常见的例子就是打印队列。比如说我们需要打印五份文档。我们会打开每个文档,然后点击打印按钮。每个文档都会被发送至打印队列。第一个发送到打印队列的文档会首先被打印,以此类推,直到打印完所有文档
// @ts-check
class Queue {
#count = 0
#lowestCount = 0
#items = {}
// 向队列添加元素
enqueue(element) {
this.#items[this.#count] = element
this.#count++
}
// 从队列移除元素
dequeue() {
if (this.isEmpty()) return undefined
const result = this.#items[this.#lowestCount]
delete this.#items[this.#lowestCount]
this.#lowestCount++
return result
}
// 查看队列头元素
peek() {
if (this.isEmpty()) return undefined
return this.#items[this.#lowestCount]
}
// 检查队列是否为空
isEmpty() {
return this.size() === 0
}
// 获取它的长度
size() {
return this.#count - this.#lowestCount
}
// 清空队列
clear() {
this.#items = {}
this.#count = 0
this.#lowestCount = 0
}
// 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
}
}
const queue = new Queue()
console.log(queue.isEmpty())
queue.enqueue('John')
queue.enqueue('Jack')
console.log(queue)
console.log(queue.toString())
queue.enqueue('Camila')
console.log(queue.toString())
console.log(queue.size())
console.log(queue.isEmpty())
queue.dequeue()
queue.dequeue()
console.log(queue.toString())
5.2 双端队列数据结构
双端队列(deque,或称 double-ended queue)是一种允许我们同时从前端和后端添加和移除元素的特殊队列。
在计算机科学中,双端队列的一个常见应用是存储一系列的撤销操作。每当用户在软件中进行了一个操作,该操作会被存在一个双端队列中(就像在一个栈里)。当用户点击撤销按钮时,该操作会被从双端队列中弹出,表示它被从后面移除了。在进行了预先定义的一定数量的操作后,最先进行的操作会被从双端队列的前端移除。由于双端队列同时遵守了先进先出和后进先出原则,可以说它是把队列和栈相结合的一种数据结构。
class Deque {
#count = 0
#lowestCount = 0
#items = {}
// 向双端队列的前端添加元素
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
}
}
addBack(element) {
this.#items[this.#count] = element
this.#count++
}
isEmpty() {
return this.#count - this.#lowestCount === 0
}
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
}
size() {
return this.#count - this.#lowestCount
}
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
const result = this.#items[this.#count]
delete this.#items[this.#count]
this.#count--
return result
}
peekFront() {
return this.#items[this.#lowestCount]
}
peekBack() {
return this.#items[this.#count]
}
clear(){
this.#count = 0
this.#lowestCount = 0
this.#items = {}
}
}
const deque = new Deque()
console.log(deque.isEmpty())
deque.addBack('John')
deque.addBack('Jack')
console.log(deque.toString())
deque.addBack('Camila')
console.log(deque.toString())
console.log(deque.size())
console.log(deque.isEmpty())
deque.removeFront()
console.log(deque.toString())
deque.removeBack()
console.log(deque.toString())
deque.addFront('John')
console.log(deque.toString())
5.3 使用队列和双端队列来解决问题
5.3.1 循环队列 --- 击鼓传花游戏
5.3.2 回文检查器
回文是正反都能读通的单词、词组、数或一系列字符的序列,例如 madam 或 racecar。