今天继上次的栈结构之后,继续推出队列结构。队列结构也是我们在开发中经常用到的一种数据结构。比如js的事件循环队列和Nodejs中的事件循环队列。这也是面试经常考察的考点之一。
1.首先我们来实现一下队列
/**
* enqueue(elements) 在队列尾部添加一个新的项
* dequeue() 移除队列的第一项(即排在队列最前面的项)并返回移除的元素
* peek() 返回队列中第一个元素 -- 最先被添加的元素,也将是最先被移除的元素
* isEmpty() 队列中不包含任何元素 返回true 否则 返回false
* size() 返回队列包含的元素个数
* clear() 清空队列
* toString()
*/
class Queue {
constructor() {
this.count = 0
this.lowestCount = 0
this.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.count - this.lowestCount === 0
}
size() {
return this.count - this.lowestCount
}
clear() {
this.count = 0
this.lowestCount = 0
this.items = {}
}
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(3)
queue.enqueue(5)
console.log(queue.toString())
console.log(queue.peek())
console.log(queue.dequeue())
2.根据队列解决一个现实中的例子,击鼓传花
function hotPotato(elementsList, num) {
const queue = new Queue()
const elimitatedList = []
for (let i = 0; i < elementsList.length; i++) {
queue.enqueue(elementsList[i])
}
while (queue.size() > 1) {
for (let i = 0; i < num; i++) {
queue.enqueue(queue.dequeue())
}
elimitatedList.push(queue.dequeue())
}
return {
eliminated: elimitatedList,
winner: queue.dequeue()
}
}
const names = ['John','Jack','Camila','Ingrid','Carl']
const result = hotPotato(names,7)
result.eliminated.forEach(name=>{
console.log(`${name}在击鼓传花游戏中被淘汰`)
})
console.log(`胜利者:${result.winner}`)
3.实现一个双端队列,双端队列既有队列的特点也有栈的特点,实现起来稍微有点复杂,不过只要积极思考,还是很清爽的。
/**
* addFront(element) 在双端队列前端添加新的元素
* addBack(element) 在双端队列的后端添加新的元素(同enqueue)
* removeFront() 从双端队列的前端移除第一个元素(同dequeue)
* removeBack() 从双端队列的后端移除第一个元素(同pop)
* peekFront() 返回双端队列前端的第一个元素(同Queue类中的peek)
* peekBack() 返回双端队列后端的第一个元素(同Stack类中的peek)
* isEmpty() 队列中不包含任何元素 返回true 否则 返回false
* size() 返回队列包含的元素个数
* clear() 清空队列
* toString()
*/
class Deque {
constructor() {
this.count = 0
this.lowestCount = 0
this.items = {}
}
isEmpty() {
return this.count - this.lowestCount === 0
}
clear() {
this.count = 0
this.lowestCount = 0
this.items = {}
}
size() {
return this.count - this.lowestCount
}
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
}
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++
}
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
}
peekFront() {
if (this.isEmpty()) {
return undefined
}
return this.items[this.lowestCount]
}
peekBack() {
if (this.isEmpty()) {
return undefined
}
return this.items[--this.count]
}
}
4.根据双端对列我们来解决一个现实中的例子,判断一段文字是否是回文(一段文字不管是正向读还是反向读都是一样的,比如‘a’,'aa','aba')。
/**
* 验证一段文字是否回文
* @param {*} aString 字符串
* @returns
*/
function palindromeChecker(aString) {
if (aString === undefined || aString === null || (aString !== null && aString.length === 0)) { return false }
const deque = new Deque()
const lowerString = aString.toLocaleLowerCase().split('').join('');
let isEqual = true;
let firstChar, lastChar;
for (let i = 0; i < lowerString.length; i++) {
deque.addBack(lowerString.charAt(i));
}
while (deque.size() > 1 && isEqual) {
firstChar = deque.removeFront()
lastChar = deque.removeBack()
if (firstChar !== lastChar) {
isEqual = false
}
}
return isEqual
}
console.log(palindromeChecker('a')) // true
console.log(palindromeChecker('aa')) // true
console.log(palindromeChecker('aba')) // true
console.log(palindromeChecker('abca')) // false
console.log(palindromeChecker('上海自来水来自海上')) // true