1. 程式人生 > 其它 >21-2-資料結構-佇列/優先佇列

21-2-資料結構-佇列/優先佇列

佇列是一種先進先出的資料結。佇列只能在隊尾插入元素,在隊首刪除元素,這點和棧不一樣。它用於儲存順序排列的資料。佇列就像我們日常中的排隊一樣,排在最前面的第一個辦理業務,新來的人只能在後面排隊。佇列這種資料結構在程式設計中被用到很多地方。

定義佇列的操作

 

 

 上圖展示了佇列的兩個操作,入隊和出隊。出隊操作是刪除對頭的元素,入隊操作是在隊尾新增元素。除此之外我們還需要能夠獲取隊首和隊尾的元素或者全佇列的元素,還需要知道佇列的長度,還需要一個方法來清空佇列,由此,我們定義佇列的方法。

enqueue()     入隊
dequeue()     出隊
front()          返回隊首
back()          返回隊尾
toString()      返回所有佇列中所有元素
length()        返回佇列的長度
clear()          清空佇列

佇列的實現

javascript中的陣列具有其他程式語言的陣列沒有的缺點,直接只用陣列的push()方法就可以在陣列末端加入元素,使用shift()方法可以刪除陣列的第一個元素。使用陣列來實現佇列順理成章。

準備開始實現對列Queue類,先從建構函式開始;

function Queue() {
    this.dataStore = [];
    this.enqueue = enqueue;
    this.dequeue = dequeue;
    this.front = front;
    this.back = back;
    this.toString = toString;
    
this.length = length; }

接下來開始實現對列的各個方法。

enqueue()方法向隊尾新增一個元素,dequeue()方法刪除隊首的元素:

function enqueue(element) {
    this.dataStore.push(element);   // 把新來的元素放在隊尾
}

function dequeue() {
    return this.dataStore.shift();    // 刪除隊首元素,並把這個元素返回
}

front()和back()方法分別是返回佇列的隊首和隊尾的元素:

function front() {
    return
this.dataStore[0]; // 返回隊首元素 } function back() { return this.dataStore[this.dataStore.length - 1]; // 返回隊尾元素 }

toString() 方法顯示佇列內的所有元素: 

function toString() {
    var retStr = '';
    this.dataStore.forEach(val => {   // 遍歷佇列,把佇列中的所有元素拼成字串返回
        retStr += val + '\n';
    });
    return retStr;
}

length() 方法返回佇列長度,clear()方法清空佇列:

function length() {
    return this.dataStore.length;
}

function clear() {
    this.dataStore.length = 0;  // 通過把佇列的長置0來清空佇列
}

到此,佇列這種資料結構藉助javascript已經實現了。接下來測試一下。

const q = new Queue();
q.enqueue('java');
q.enqueue('php');
q.enqueue('python');
console.log('佇列裡的所有元素:');
console.log(q.toString());
q.dequeue()
console.log('出隊之後所有元素:');
console.log(q.toString());
console.log('隊首元素:' + q.front());
console.log('隊尾元素:' + q.back());
console.log('佇列長度:' + q.length());
q.clear();
console.log('清空佇列後的長度:' + q.length());

輸出結果:


優先佇列

在一般情況下,從佇列中刪除的元素,一定是率先入隊的元素。但有的特殊情況不用遵守先進先出的約定。比如急診室裡的佇列,醫生會根據病人病情的嚴重程度來決定服務的優先順序。在這種佇列裡,從佇列中刪除的元素就有可能不是最先入隊的元素。這種情況,就需要使用一個叫優先佇列的資料結構來模擬。

  接下來,我們以急診室排隊為例,實現以下優先佇列。

  在急診室的候診室裡,分診護士會評估患者病情的嚴重程度,然後給一個優先順序程式碼,高優先順序的患者先於低優先順序的患 者就醫,同樣優先順序的患者按照先來先服務的順序就醫。

  先來定義儲存佇列元素的物件,然後再構建我們的優先佇列系統:

 

function Patient(name, code) {
    this.name = name;
    this.code = code;    // 優先順序程式碼,整數,代表患者優先順序
}

現在需要重新定義 dequeue() 方法,使其刪除佇列中擁有最高優先順序的元素。我們規定優先碼的值最小的元素優先順序最高。新的 dequeue() 方法遍歷佇列的底層儲存陣列,從中找出優先碼最小的元素,然後刪除該元素。新的dequeque()方法定義如下所示:

function dequeue() {
    let priority = this.dataStore[0].code;
    let pos = 0;
    // 使用簡單的順序查詢方法尋找優先順序最高的元素
    this.dataStore.forEach((p, i) => {
        if (p.code < priority) {
            priority = p.code;
            pos = i;
        }
    });
    // 返回從佇列中刪除的元素
    return this.dataStore.splice(pos, 1);
}

最後,重新實現toString()方法來顯示Patient物件。

function toString() {
    var retStr = "";
    this.dataStore.forEach(p => {
        retStr += `${p.name} 優先順序: ${p.code}\n`;
    });
    return retStr;
}

到此,模擬的急診優先佇列已經實現完成,我們測試一下。

var p = new Patient("小一", 5);
const ed = new Queue();
ed.enqueue(p);
p = new Patient("小二", 4);
ed.enqueue(p);
p = new Patient("小三", 6);
ed.enqueue(p);
p = new Patient("小四", 1);
ed.enqueue(p);
p = new Patient("小五", 1);
console.log('正在排隊的人:');
console.log(ed.toString());
// 第一輪
ed.enqueue(p);
var seen = ed.dequeue();
console.log('正在被接診的病人:' + seen[0].name);
console.log('等待的人:')
console.log(ed.toString());
// 下一輪
var seen = ed.dequeue();
console.log('正在被接診的病人:' + seen[0].name);
console.log('等待的人:')
console.log(ed.toString());
// 下一輪
var seen = ed.dequeue();
console.log('正在被接診的病人:' + seen[0].name);
console.log('等待的人:')
console.log(ed.toString());

輸出:

 

 可以看到,待診病人按照優先順序,一個一個的從佇列中被接診。