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
相關文章
相關標籤/搜索