JS中队列和双端队列实现及应用

队列

  • 队列
  • 双端队列数据结构
  • 应用前端

    • 用击鼓传花游戏模拟循环队列
    • 用双端对列检查一个词是否构成回文
    • 生成 1 到 n 的二进制数

队列和双端队列

队列遵循先进后出(FIFO, 也称为先来先服务) 原则的. 平常有不少这样场景: 排队购票、银行排队等.
由对列的特性,银行排队为例, 队列应该包含以下基本操做:后端

  • 加入队列(取号) enqueue
  • 从队列中移除(办理业务离开) dequeue
  • 当前排队号码(呼叫下一我的) peek
  • 当前队列长度(当前排队人数) size
  • 判断队列是否是空 isEmpty
class Queue {
    constructor() {
        // 队列长度, 类数组 length
        this.count = 0
        // 队列中全部项
        this.items = {}
        // 记录对列头, 类数组 index
        this.lowestCount = 0
    }

    enqueue(ele) {
        this.items[this.count++] = ele
    }

    dequeue() {
        if (this.isEnpty()) {
            return undefined
        }
        const ele = this.items[this.lowestCount]
        delete this.items[this.lowestCount]
        this.lowestCount++
        return ele
    }

    peek() {
        if (this.isEnpty()) {
            return
        }
        return this.items[this.lowestCount]
    }

    size() {
        /**
        * 当队列为非空时:
        * 1. count 是长度
        * 2. lowestCount 是下标
        * 二者关系应该 lowestCount = count - 1
        */
        return this.count - this.lowestCount
    }

    isEnpty() {
        return this.size() == 0
    }

    clear() {
        this.items = {}
        this.lowestCount = 0
        this.count = 0
    }

    toString() {
        if (this.isEnpty()) {
            return ''
        }
        let objString = `${this.items[this.lowestCount]}`
        for (let i = this.lowestCount + 1; i < this.count; i++) {
        objString = `${objString}, ${this.items[i]}`
        }
        return objString
   }

}

双端队列(deque 或 double-ended queue)

什么是双端队列?数组

容许从前端(front)和后端(rear)添加元素, 遵循的原则先进先出或后进先出.

双端队列能够理解为就是栈(后进先出)和队列(先进先出)的一种结合体. 既然是结合那么相应的操做也支持队列,栈的操做. 下面咱们定义一个Deque数据结构

  • addFront
  • removeFront
  • addBack
  • removeBack
  • clear
  • isEmpty
  • peekFront
  • prekBack
  • size
  • toString
class Deque {
    constructor() {
        this.items = {}
        this.count = 0
        this.lowestCount = 0
    }

    addFront(ele) {
        if (this.isEmpty()) {
            this.items[this.count] = ele
        } else if (this.lowestCount > 0) {
            this.lowestCount -= 1
            this.items[this.lowestCount] = ele
        } else {
            for (let i = this.count; i > 0; i--) {
                this.items[i] = this.items[i - 1]
            }
            this.items[0] = ele
        }
            this.count++
            return ele
        }

    removeFront() {
        if (this.isEmpty()) {
            return
        }
        const delEle = this.items[this.lowestCount]
        delete this.items[this.lowestCount]
        this.lowestCount++
        return delEle
    }

    addBack(ele) {
        this.items[this.count] = ele
        this.count++
    }

    removeBack() {
        if (this.isEmpty()) {
            return
        }

        const delEle = this.items[this.count - 1]
        delete this.items[this.count - 1]
        this.count--
        return delEle
    }

    peekFront() {
        if (this.isEmpty()) {
            return
        }
        return this.items[this.lowestCount]
    }

    peekBack() {
        if (this.isEmpty()) {
            return
        }
        return this.items[this.count - 1]
    }

    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
    }

}

队列的应用

击鼓传花游戏

击鼓传花游戏: 简单描述就是一群人围成一个圈传递花,喊停的时花在谁手上就将被淘汰(每一个人均可能在前端,每一个参与者在队列位置会不断变化),最后只剩下一个时就是赢者. 更加详细能够自行查阅.dom

下面经过代码实现:this

function hotPotato(elementsList, num) {
    // 建立一个容器
    const queue = new Queue()
    const elimitatedList = []
    // 把元素(参赛者)加入队列中
    for (let i = 0, len = elementsList.length; i < len; i++) {
        queue.enqueue(elementsList[i])
    }

    /**
    * 击鼓传花
    * 首先队列规则: 先进先出
    * 那么在传花过程当中,任何一个元素均可能是前端, 在传花的过程当中应该就是前端位置不断变化.
    * 当喊停的时(num 循环完), 也就是花落在谁手(谁在前端)则会被淘汰*(移除队列)
    */

    while (queue.size() > 1) {
        for (let j = 0; j < num; j++) {
            queue.enqueue(queue.dequeue())
        }
        elimitatedList.push(queue.dequeue())
    }
    return {
        winer: queue.dequeue(),
        elimitatedList
    }
}

代码运行以下:spa

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

console.log(hotPotato(arr, Math.ceil(Math.random() * 10))) // { winer: 5, elimitatedList: [4, 8, 2, 7, 3,10, 9, 1, 6]}
console.log(hotPotato(arr, Math.ceil(Math.random() * 10))) // { winer: 5, elimitatedList: [4, 8, 2, 7, 3,10, 9, 1, 6]}
console.log(hotPotato(arr, Math.ceil(Math.random() * 10))) // { winer: 8, elimitatedList: [10, 1, 3, 6, 2,9, 5, 7, 4]}

判断回文

上一篇栈中也有涉及回文的实现, 下面咱们经过双端队列来实现一样的功能.code

function palindromeChecker(aString) {
    if (!aString || typeof aString !== 'string' || !aString.trim().length) {
        return false
    }
    const deque = new Deque()
    const lowerString = aString.toLowerCase().split(' ').join('')

    // 加入队列

    for (let i = 0; i < lowerString.length; i++) {
        deque.addBack(lowerString[i])
    }

    let isEqual = true
    let firstChar = ''
    let lastChar = ''

    while (deque.size() > 1 && isEqual) {
        firstChar = deque.removeFront()
        lastChar = deque.removeBack()
        if (firstChar != lastChar) {
            isEqual = false
        }
    }

    return isEqual

}

下面经过代码演示下:blog

console.log(palindromeChecker('abcba')) // true 当前为回文

image

生成 1 到 n 的二进制数

function generatePrintBinary(n) {
    var q = new Queue()
    q.enqueue('1')
    while (n-- > 0) {
        var s1 = q.peek()
        q.dequeue()
        console.log(s1)
        var s2 = s1
        q.enqueue(s1 + '0')
        q.enqueue(s2 + '1')
    }
}

generatePrintBinary(5) // => 1 10 11 100 101
相关文章
相关标签/搜索