第五章:javascript:隊列


隊列是一種列表,不同的是隊列只能在末尾插入元素,在隊首刪除元素。隊列用於存儲按順序排列的數據。先進先出。這點和棧不一樣,在棧中,最后入棧的元素反被優先處理。可以將隊列想象成銀行排隊辦理業務的人,排隊在第一個的人先辦理業務,其它人只能排着,直到輪到他們為止

隊列是一種先進先出(FIFO)的數據結構。隊列被用在很多地方。比如提交操作系統執行一系列進程。打印任務池等。一些仿真系統用來模擬銀行或雜貨店里排隊的顧客。

一,隊列的操作。

隊列的兩種主要操作是:向隊列中插入新元素和刪除隊列中的元素。插入操作也叫做入隊。刪除元素也叫做出隊。入隊是在末尾插入新元素,出隊是刪除隊頭的元素

隊列的另外一項操作是讀取隊頭的元素,這個操作叫peek()。該操作返回隊頭的元素,但不把它從隊列中刪除。除了讀取隊頭的元素,我們想知道隊列中有多少元素,可以使用length屬性滿足要求;要想清空隊列中所有元素。可以使用clear()方法來實現。

二,一個數組實現的隊列。

使用數組來實現隊列看起來順理成章。javascript中的數組具有其它編程語言中沒有的優點,數組的push()可以在數組末尾加入元素,數組的shift()方法則可以刪除數組的第一個元素。

push()方法將它的參數插入數組中第一個開放的位置,該位置總在數組的末尾,即使是個空數組也是如此。

    names = [];
    names.push("Cny");
    names.push("Jen");
    console.log(names);//["Cny", "Jen"]

然后使用shift()方法刪除數組的第一個元素:

    names.shift();
    console.log(names);//["Jen"]

准備開始實現Queue類。先從構造函數開始:

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

enqueue()方法向隊末尾添加一個元素:

    function enqueue(element) {
        this.dataStore.push(element)
    }

dequeue方法刪除隊首的元素

    function dequeue() {
        return this.dataStore.shift();
    }

可以使用以下方法讀取隊首和隊末的元素

    function front() {
        return this.dataStore[0];
    }

    function back() {
        return this.dataStore[this.dataStore.length - 1]
    }

還需要toString()方法顯示隊列內的所有元素

    function toString() {
        var retStr = "";
        for (var i = 0; i < this.dataStore.length; ++i ) {
            retStr += this.dataStore[i] + "\n";
        }
        return retStr
    }

最后,需要一個方法判斷隊列是否為空

    function empty() {
        if (this.dataStore.length == 0) {
            return true;
        } else {
            return false;
        }
    }

Queue類的定義和測試

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

    //向隊末尾添加一個元素
    function enqueue(element) {
        this.dataStore.push(element)
    }

    //刪除隊首的元素
    function dequeue() {
        return this.dataStore.shift();
    }

    function front() { //讀取隊首和隊末的元素
        return this.dataStore[0];
    }
    function back() { ////讀取隊首和隊末的元素
        return this.dataStore[this.dataStore.length - 1]
    }

    //顯示隊列內的所有元素
    function toString() {
        var retStr = "";
        for (var i = 0; i < this.dataStore.length; ++i ) {
            retStr += this.dataStore[i] + "\n";
        }
        return retStr
    }

    //隊列是否為空
    function empty() {
        if (this.dataStore.length == 0) {
            return true;
        } else {
            return false;
        }
    }


    //測試程序

    var q = new Queue();
    q.enqueue("Me");
    q.enqueue("Her");
    q.enqueue("His");
    console.log(q.toString()); 
    q.dequeue();
    console.log(q.toString());

    console.log("第一個元素是: " + q.front());
    console.log("最后一個元素是: " + q.back())


三,使用隊列,方塊舞和舞伴的分配問題

前面我們提到過,經常用隊列模擬排隊的人。下面,我們使用隊列來模擬跳方塊舞的人。當男男女女來到舞池,他們按照自己的性別排成兩隊。當舞池中有地方空出來的時候,兩個隊列中的第一個人組成舞伴。他們身后的人各自向前一位,組成新的隊首。當一對新的舞伴進入舞池時,主持人會大喊出他們的名字。當一對舞伴走出舞池,且兩隊隊伍任意一隊沒有人時,主持人會把這個情況告訴大家。

為了模擬這個情況,我們將男男女女姓名存儲在一個文本文件。

女 小李
男 福來
男 強子
男 李勇
女 小梅
男 來福
女 艾麗
男 張帆
男 文強
男 丁力
女 娜娜

每個舞者的信息都被保存在一個Dancer對象中:

    function Dancer(name, sex) {
        this.name = name;
        this.sex = sex;
    }

我們需要一個函數,將舞者的信息讀到程序中來。

    function getDancers(males, females) {
        //var names = _names.split("\n");
        var names = _names.split("**");
        for (var i = 0; i < names.length; ++i) {
            names[i] = names[i].trim();
        }

        for (var i = 0; i < names.length; ++i) {
            var dancer = names[i].split(" ");
            var sex = dancer[0];
            var name = dancer[1];
            if (sex == "女") {
                females.enqueue(new Dancer(name, sex));
            } else {
                males.enqueue(new Dancer(name, sex))
            }
        }
    }

此函數將舞者按照性別分在不同的隊列中。

下一個函數將男性和女性組成舞伴,並宣布配對結果:

    function dance(males, females) {
        console.log("這組舞伴是:")
        while (!females.empty() && !males.empty()) {
            person = females.dequeue();
            console.log("女舞者是" + person.name);

            person = males.dequeue();
            console.log("男舞者是" + person.name);
        }
    } 

我們可能對該程序做修改,想顯示排隊時男性女性的數量,隊列中目前沒有顯示元素個數的方法。現在加入

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

綜合測試:

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

    //向隊末尾添加一個元素
    function enqueue(element) {
        this.dataStore.push(element)
    }

    //刪除隊首的元素
    function dequeue() {
        return this.dataStore.shift();
    }

    function front() { //讀取隊首和隊末的元素
        return this.dataStore[0];
    }
    function back() { ////讀取隊首和隊末的元素
        return this.dataStore[this.dataStore.length - 1]
    }

    //顯示隊列內的所有元素
    function toString() {
        var retStr = "";
        for (var i = 0; i < this.dataStore.length; ++i ) {
            retStr += this.dataStore[i] + "\n";
        }
        return retStr
    }

    //隊列是否為空
    function empty() {
        if (this.dataStore.length == 0) {
            return true;
        } else {
            return false;
        }
    }

    //隊列個數
    function count(){
        return this.dataStore.length;
    }

    function Dancer(name, sex) {
        this.name = name;
        this.sex = sex;
    }

    function getDancers(males, females) {
        //var names = _names.split("\n");
        var names = _names.split("**");
        for (var i = 0; i < names.length; ++i) {
            names[i] = names[i].trim();
        }

        for (var i = 0; i < names.length; ++i) {
            var dancer = names[i].split(" ");
            var sex = dancer[0];
            var name = dancer[1];
            if (sex == "女") {
                females.enqueue(new Dancer(name, sex));
            } else {
                males.enqueue(new Dancer(name, sex))
            }
        }
    }


    function dance(males, females) {
        console.log("這組舞伴是:")
        while (!females.empty() && !males.empty()) {
            person = females.dequeue();
            console.log("女舞者是" + person.name);

            person = males.dequeue();
            console.log("男舞者是" + person.name);
        }
    } 

    //測試程序

    var _names = "女 小李**男 福來**男 強子**男 李勇**女 小梅**男 來福**女 艾麗**男 張帆**男 文強**男 丁力**女 娜娜";

    var names = _names.split("**");


    //測試程序
    var maleDancer = new Queue();
    var femaleDancer = new Queue();

    getDancers(maleDancer, femaleDancer);
    dance(maleDancer, femaleDancer)

    if (!femaleDancer.empty()) {
        console.log(femaleDancer.front().name + "正在等待跳舞")
    }

    if (!maleDancer.empty()) {
        console.log(maleDancer.front().name + "正在等待跳舞")
    }

    //顯示等待跳舞的人個數

    var nanDancers = new Queue();
    var nvDancers = new Queue();
    getDancers(nanDancers, nvDancers)
    dance(nanDancers,nvDancers);
    if (nanDancers.count() > 0) {
        console.log("有" + nanDancers.count() + "男舞者等待")
    }

    if (nvDancers.count() > 0) {
        console.log("有" + mvDancers.count() + "女舞者等待")
    }

 

四:使用隊列對數據進行排序

隊列不僅用於執行顯示生活中與排隊有關的操作,還可以用於對數據進行排序。計算機剛出現時,程序是通過穿孔卡輸入主機的,每張卡包含一條程序語句。這些穿孔卡裝在一個盒子里,經過一個繼續裝置進行排序。我們可以使用一組隊列來模擬這一過程。這種技術叫基數排序它不是最快的排序算法,但是它展示了一些有趣的隊列使用方法

對於0-99的數字,基數排序將數據掃描兩次。第一次按個位上的數字進行排序,第二次按照十位上的數字進行排序。每個數字根據對於位上的數值被分在不同的盒子里。假設有如下數字:

91,46,85,15,92,35,31,22

經過基數排序第一次掃描后,數字被分配到以下盒子里

Bin 0 : 
Bin 1 : 91, 31
Bin 2 : 82, 22
Bin 3 : 
Bin 4 :
Bin 5 : 85, 35
Bin 6 : 46
Bin 7 :
Bin 8 :
Bin 9 :

根據盒子的順序,對數字第一次排序的結果如下

91,31,92,22,85,15,25,46

根據十位上的數字,再次排序。

Bin 0 : 
Bin 1 : 15
Bin 2 : 22
Bin 3 : 31, 35
Bin 4 : 46
Bin 5 : 
Bin 6 : 
Bin 7 :
Bin 8 : 85
Bin 9 : 91, 92

最后,將盒子中的數字去除,組成一個新的列表,該列表即為排好序的數字:

15, 22, 31, 35, 46, 85, 91, 92

使用隊列代表盒子,可以實現這個算法。我們需要9個隊列,每一個對應一個數字。所有隊列保存在一個數組中,使用取余和除法操作決定個位和十位。算法的剩余部分將數字加入相應隊列,根據個位數值對其從新排序,然后根據十位上的數值進行排序。結果即為排列好的數字

下面是根據 相應位(個位或十位)上的數值,將數字分配到相應隊列的函數:

    function distribute(nums, queues, n, digit) { //digit表示個位和十位上的值
        for (var i = 0 ; i < n ; ++i) {
            if (digit == 1) {
                queues[nums[i]%10].enqueue(nums[i]);
            } else {
                queues[Math.floor(nums[i]/10).enqueue(nums[i])]
            }
        }
    }

下面是從隊列中收集數字的函數

    function collect(queues, nums){
        var i = 0;
        for (var digit = 0; digit < 10; ++digit) {
            while(!queues[digit].empty()){
                nums[i++] = queues[digit].dequeue()
            }
        }
    }

附上測試代碼

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

    //向隊末尾添加一個元素
    function enqueue(element) {
        this.dataStore.push(element)
    }

    //刪除隊首的元素
    function dequeue() {
        return this.dataStore.shift();
    }

    function front() { //讀取隊首和隊末的元素
        return this.dataStore[0];
    }
    function back() { ////讀取隊首和隊末的元素
        return this.dataStore[this.dataStore.length - 1]
    }

    //顯示隊列內的所有元素
    function toString() {
        var retStr = "";
        for (var i = 0; i < this.dataStore.length; ++i ) {
            retStr += this.dataStore[i] + "\n";
        }
        return retStr
    }

    //隊列是否為空
    function empty() {
        if (this.dataStore.length == 0) {
            return true;
        } else {
            return false;
        }
    }

    //隊列個數
    function count(){
        return this.dataStore.length;
    }

    //基礎排序程序
    function distribute(nums, queues, n, digit) { //digit表示個位和十位上的值
        for (var i = 0 ; i < n ; ++i) {
            if (digit == 1) {
                queues[nums[i]%10].enqueue(nums[i]);
            } else {
                queues[Math.floor(nums[i] / 10)].enqueue(nums[i])
            }
        }
    }

    function collect(queues, nums){
        var i = 0;
        for (var digit = 0; digit < 10; ++digit) {
            while(!queues[digit].empty()){
                nums[i++] = queues[digit].dequeue()
            }
        }
    }

    function dispArray(arr) {
        for (var i = 0; i < arr.length; ++i) {
            console.log(arr[i] + " ")
        }
    }

    //主程序

    var queues = []
    for (var i = 0; i < 10; ++i) {
        queues[i] = new Queue();
    }
    var nums = []
    for (var i = 0; i < 10; ++i){
        nums[i] = Math.floor(Math.floor(Math.random() * 101))
    }

    console.log("之前的基數:")
    dispArray(nums);
    distribute(nums, queues, 10, 1);
    collect(queues, nums);
    distribute(nums,queues, 10 ,10);
    collect(queues, nums);
    console.log("之后的基數:")
    dispArray(nums)

五:優先隊列方法

在一般的情況下,從隊列中刪除的元素,一定是率先入隊的元素。但是也有一些使用隊列的應用 ,在刪除元素時不必遵守先進先出的約定。這周應用,需要使用一個叫優先隊列的數據結構來進行模擬。

從優先隊列中刪除元素時,需要考慮優先權的限制。比如醫院急診科的候診室,就是一個采用優先隊列的例子。當病人進入候診室,分診會評估病情的嚴重程度,然后給一個優先級代碼。高優先級的患者優先於低優先級者就醫,同樣優先級的患者按照優先來服務的順序就醫。

先來定義存儲隊列元素的對象,然后再構建我們的優先隊列系統。

    function Patient(name, code){
        this.name = name;
        this.code = code;
    }

變量code是一個整數,表示優先級

現在需要從新定義dequeue()方法。使其刪除隊列中優先級最高級的元素。我們規定:優先碼的值最小的元素優先級最高。新的dequeue()方法遍歷隊列底層存儲數組,從中找出優先碼最低的元素,然后使用數據的splice()方法刪除優先級最高的元素。新的dequeue()方法定義如下所示:

    function dequeue() {
        var priority = this.dataStore[0].code;
        for (var i = 1; i < this.dataStore.length; ++i) {
            if (this.dataStore.[i].code < priority) {
                priority = i
            }
        }
        return this.dataStore.splice(priority,1)
    }

dequeue()方法使用簡單的順序查找方法尋找優先級最高的元素(優先碼最小優先級越高,比如1比5的優先級高)該方法返回包含一個元素的數組---從隊列中刪除的元素。
最后,定義toString()來顯示patient對象

    function toString() {
        var retStr = "";
        for (var i=0; i < this.dataStore.length; ++i) {
            retStr += this.dataStore[i].name + " code:" + this.dataStore[i].code + "\n"
        }
        return retStr
    }

 

本文完結

上一章:第四章:javascript: 棧 下一章第六章: 字典


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM