JS 隊列-優先隊列、循環隊列

《學習JavaScript數據結構與算法》讀書筆記。算法

隊列是遵行FIFO(First In First Out, 先進先出)原則的一組有序的項。隊列再尾部添加新元素,並從頂部移除元素。segmentfault

在現實中,最多見的隊列的例子就是排隊。設計模式

1.建立隊列數組

如今,咱們來建立一個類來表示一個隊列。先從最基本的聲明類開始:數據結構

function Queue(){
    // 這裏是屬性和方法
}

首先,須要一個用戶存儲隊列中元素的數據結構,咱們可使用數組。數據結構和算法

var items = [];

接下來,聲明一些隊列可用的方法:學習

  • enqueue(element(s)):進隊,向隊列尾部添加一個(或多個)新項。this

  • dequeue():移除隊列的第一項,並返回被移除的元素。設計

  • front():返回隊列中第一個元素-最早被添加,也會是最早被移除的元素。(只返回,不移除)。code

  • isEmpty():若是隊列爲空,返回true,不然,返回false。

  • size():返回隊列的長度。

首先,咱們來實現enqueue的方法,這個方法負責向隊列中添加新元素。只能是添加到隊列的尾部。

this.enqueue = function(element) {
        items.push(element);
    }

接下來要實現的是dequeue方法,這個方法負責從隊列移除項。因爲隊列遵循的是先進先出原則,因此最早移除的就是最早添加的,元素是排在數組的第一位。

this.dequeue = function() {
        return items.shift();
    }

只有enqueue方法和dequeue方法能夠添加和移除元素,這樣就確保了Queue類遵循先進先出原則。
如今來爲咱們的類實現一些額外的輔助方法:

// front():返回隊列中第一個元素
    this.front = function() {
        return items[0];
    }
    
    // isEmpty():若是隊列爲空,返回true,不然,返回false
    this.isEmpty = function() {
        return items.length === 0;
    }
    
    // size():返回隊列的長度
    this.size = function() {
        return items.length;
    }

完成,咱們的Queue類實現好了,如今來看看Queue完整的實現是怎麼樣的:

function Queue() {
    var items = [];
    
    this.enqueue = function(element) {
        items.push(element);
    }
    
    this.dequeue = function() {
        return items.shift();
    }
    
    this.front = function() {
        return items[0];
    }
    
    this.isEmpty = function() {
        return items.length === 0;
    }
    
    this.clear = function() {
        items = [];
    }
    
    this.size = function() {
        return items.length;
    }
    
    this.print = function() {
        console.log(items.toString());
    }
}

2.使用Queue類

var queue = new Queue();
console.log(queue.isEmpty()); // 輸出 true
queue.enqueue('John');        // 添加元素 John
queue.enqueue('Jam');         // 添加元素 Jam
queue.enqueue('Camila');      // 添加元素 Camila
queue.print();
console.log(queue.size);      // 輸出 3
console.log(queue.isEmpty);   // 輸出 false
queue.dequeue();              // 移除元素
queue.dequeue();            
queue.print();

運行上面的代碼,咱們能夠看出,咱們已經實現了隊列,遵循了先入先出原則。

3.優先隊列

上面咱們已經實現了一個隊列,如今,逐步深刻,咱們來看看什麼是優先隊列。

優先隊列是默認隊列的變種,它的元素的添加和移除是基於優先級的。一個現實的例子就是醫院的(急診科)候診室。醫生會優先處理病情比較嚴重的患者。

實現一個優先隊列,有兩種選擇:設置優先級,而後在正確的位置添加元素;或者用默認入列操做添加元素,任何按照優先級移除它們。下面,咱們將會在正確的位置添加元素,任何用默認你的出列操做。

function PriorityQueue() {
        var items = [];
        
        // {1}
        function QueueElement(element, priority) {
            this.element = element;
            this.priority = priority;
        }
        
        this.enqueue = function(element, priority) {
            var queueElement = new QueueElement(element, priority);
            
            if(this.isEmpty()) {
                items.push(queueElement);  // {2}
            } else {
                var added = false;
                for(var i = 0; i < items.length; i++) {
                    if(queueElement.priority < items.[i].priority) {
                        items.splice(i, 0, queueElement);    // {3}
                        added = true;
                        break;
                    }
                }
                if(!added) {    // {4}
                    items.push(queueElement);
                }
            }
        }
        
        // 其餘方法與默認隊列同樣
    }

咱們建立了一個特殊的元素(行{1}),這個元素包含了要添加到隊列的元素及其優先級。

若是隊列爲空,則直接將元素入列(行{2})。不然,就要進行比較。當找到一個比要添加的元素的priority值更大(優先級更低)時,就將元素插入到它以前(行{3})。

若是要添加的元素的priority指大於任何已有的元素,則直接將其添加到隊列的末尾(行{4})。

var priorityQueue = new PriorityQueue();
priorityQueue.enqueue('John', 2);
priorityQueue.enqueue('Jam', 1);
priorityQueue.enqueue('Sam', 1);
priorityQueue.print();

至此,咱們已經實現了優先隊列,下面,將再介紹一種隊列——循環隊列

4.循環隊列——擊鼓傳花

循環隊列是默認隊列的另外一種修改版,什麼是循環隊列呢?舉個現實中的例子,記得小時候玩過的傳花遊戲嗎?
幾個孩子圍成一圈,開始擊鼓了,孩子就把花盡快地傳遞給旁邊的人,某一時刻鼓聲中止了,傳花也就中止了,這個時候花落在誰手上,誰就被淘汰。鼓聲響起,繼續傳花,如此循環,直至只剩下一個孩子,即勝者。

function hotPotato(namelist, num) {
    var queue = new Queue();
    for (var i = 0; i < namelist.length; i++) {     // {1}
        queue.enqueue(namelist[i]);
    }
    var eliminated = "";
    while (queue.size() > 1) {                 // {2}
        for (var i = 0; i < num; i++) {
            queue.enqueue(queue.dequeue());    // {3}
        }
        eliminated = queue.dequeue();    // {4}
        console.log(eliminated + "在擊鼓傳花遊戲中被淘汰");
    }
    return queue.dequeue();    // {5}
}
var names = ['john', 'jack', 'camila', 'ingrid', 'carl'];
var winner = hotPotato(names, 7);
console.log("勝利者: " + winner);      //john

首先,先把名單添加到隊列裏面(行{1})。

當隊列的的長度大於1的時候(行{2}),根據指定的一個數字(num)迭代隊列,將隊列的頭一個移除並將其添加到隊尾(行{3})。

一旦傳遞次數達到給定的數字,則刪除此時的隊列第一項(行{4}),即拿着花的那我的,他將被淘汰。

如此循環,直至隊列的長度等於1,返回勝者(行{5})。

5.小結

經過這篇文章的介紹,咱們學會了如何用數組構造出隊列的類。同時,還掌握了很著名的優先隊列、循環隊列這兩種結構。

附:
JavaScript數據結構和算法系列:
JS 棧

JavaScript設計模式系列:
JavaScript設計模式之策略模式
JavaScript設計模式之發佈-訂閱模式(觀察者模式)-Part1

相關文章
相關標籤/搜索