数据结构----队列

271 阅读1分钟

一、什么是队列?

队列(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' 第一局游戏:

  1. 'Jhon'=》'Jack'=》'Camila'=》'Ingrid'=》'Carl'=》'Jhon'=》'Jack'=》'Camila' (Camila淘汰)
  2. 则人员变动:'Ingrid'=》'Carl'=》'Jhon'=》'Jack'=》'Ingrid'=》'Carl'=》'Jhon'=》'Jack (Jack淘汰)
  3. 'Ingrid'=》'Carl'=》'Jhon'=》'Ingrid'=》'Carl'=》'Jhon'=》'Ingrid'=》'Carl' (Carl淘汰)
  4. '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