一、什么是队列?
队列(Queue)是遵循先进先出(FIFO,也称为先来服务)原则的一组有序的项。队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。
在现实生活中,我们能想到的相似环境就是排队。
1.1 特点
先进先出,后入后出(FIFO原则)
1.2 常用方法
enqueue(element(s)): 向队列尾部添加一个(或多个)资源项dequeue: 移除队列的第一项(即排在队列最想面的项),并返回被移除的元素。peek:返回队列第一个元素 -- 最先被添加,也将是最先被移除的元素。队列不做任何变动isEmpty: 判断队列是否为空size:队列的长度clear: 清空队列
二、 js实现队列结构
export 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]
}
size() {
return this.count - this.lowestCount
}
isEmpty() {
return this.size() === 0
}
clear() {
this.items = {}
this.count = 0
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
}
}
测试数据准确性相关代码:
import { Queue } from "./queue.js";
const queue = new Queue()
console.log(queue.isEmpty()) //true
queue.enqueue('John');
queue.enqueue('Jack')
console.log(queue.toString()) //John,Jack
queue.enqueue('Cammila')
console.log(queue.toString()) //John,Jack,Cammila
console.log(queue.size()) //3
console.log(queue.isEmpty()) // false
queue.dequeue()
queue.dequeue()
console.log(queue.toString()) //Cammila
三、双端队列数据结构
双端队列(deque,或称double-ended queue)是一种允许我们同时从前端和后端添加和移除元素的特殊队列。
现实生活中的例子:有电影院、餐厅中排队的队伍等,一个刚买了票的人如果只是还需要在问一些简单的信息,就可以直接回到队伍的头部。另外,在队伍末尾的人如果赶时间,他可以直接离开队伍
一些常用的方法
- addFront(element):该方法在双端队列前端添加新的元素
- addBack(element):该方法在双端队列后端添加新的元素(实现方法和Queue类中的enqueue方法相同)
- removeFront():该方法在双端队列前端移除第一个元素(实现方法和Queue类中的dequeue方法相同)
- removeBack():该方法在双端队列后端添加新的元素(实现方法和Stack类中的pop方法一样)
- peekFront():该方法返回双端队列前端的第一个元素(实现方法和Queue类中的peek方法相同)
- peekBack():该方法返回双端队列后端的第一个元素(实现方法和Stack类中的peek方法一样)
export class Deque{
constructor(){
this.count = 0;
this.lowestCount = 0;
this.items = {}
}
size() {
return this.count - this.lowestCount
}
isEmpty() {
return this.size() === 0
}
clear() {
this.items = {}
this.count = 0
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
}
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-1]
}
}
测试数据准确性相关代码
import { Deque } from "./deque.js";
const deque = new Deque();
console.log(deque.isEmpty()); // outputs true
deque.addBack('John');
deque.addBack('Jack');
console.log(deque.toString()); // John,Jack
deque.addBack('Camila');
console.log(deque.toString()); // John,Jack,Camila
console.log(deque.size()); // outputs 3
console.log(deque.isEmpty()); // outputs false
deque.removeFront(); // remove John
console.log(deque.toString()); // Jack,Camila
deque.removeBack(); // Camila decides to leave
console.log(deque.toString()); // Jack
deque.addFront('John'); // John comes back for information
console.log(deque.toString()); // John,Jack
四、 使用队列和双端队列来解决问题
4.1 循环队列--击鼓传花游戏
import { Queue } from './queue.js'
function hotPotato(elementList, num) {
const queue = new Queue()
const eliminatedList = []
for (let i = 0; i < elementList.length; i++) {
queue.enqueue(elementList[i])
}
console.log(queue)
while(queue.size()>1){
for(let i =0;i<num;i++){
queue.enqueue(queue.dequeue())
}
eliminatedList.push(queue.dequeue())
console.log('while',queue,eliminatedList)
}
return {
eliminated:eliminatedList,
winner:queue.dequeue()
}
}
const name = ['Jhon','Jack','Camila','Ingrid','Carl']
const result = hotPotato(name,7)
console.log('come ',result) // { eliminated: [ 'Camila', 'Jack', 'Carl', 'Ingrid' ], winner: 'Jhon' }
'Jhon','Jack','Camila','Ingrid','Carl' 第一局游戏:
- 'Jhon'=》'Jack'=》'Camila'=》'Ingrid'=》'Carl'=》'Jhon'=》'Jack'=》'Camila' (Camila淘汰)
- 则人员变动:'Ingrid'=》'Carl'=》'Jhon'=》'Jack'=》'Ingrid'=》'Carl'=》'Jhon'=》'Jack (Jack淘汰)
- 'Ingrid'=》'Carl'=》'Jhon'=》'Ingrid'=》'Carl'=》'Jhon'=》'Ingrid'=》'Carl' (Carl淘汰)
- 'Jhon'=》'Ingrid'=》'Jhon'=》'Ingrid'=》'Jhon'=》'Ingrid'=》'Jhon'=》'Ingrid' (Ingrid淘汰)
4.2 回文检查器
**回文:**是正反都能读通的单词、词组、数或一系列字符的序列,如madam和racecar
采用双端队列实现此算法
import { Deque } from './deque.js'
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.addFront(lowerString[i])
}
while(deque.size() > 1 && isEqual){
firstChar = deque.removeFront()
lastChar = deque.removeBack()
if(firstChar != lastChar){
isEqual = false
}
}
return isEqual
}
palindromeChecker('Ma dam') // true
palindromeChecker('abcdef') // false