1. 程式人生 > >資料結構Java語言描述之迴圈佇列的兩種實現方式

資料結構Java語言描述之迴圈佇列的兩種實現方式

1、佇列的描述

佇列是一種先進先出的儲存資料的結構。如我們現實生活中的排隊就是一個典型的例子。

2、迴圈佇列及其優點

  2.1、迴圈佇列是佇列的擴充套件,就是佇列首尾連線,形成一個閉環的圈子。

  2.2、優點

  充分利用儲存空間。

3、佇列的實現方式

  3.1、佇列與棧的實現方式一樣,一般分為兩種:線性佇列與鏈式佇列。

  3.2、線性佇列的描述

 線性佇列又可以分為普通佇列與迴圈佇列兩種。

  3.2.1、普通佇列

  以int值1,2,3,4,5分別進行put佇列操作如下圖所示

                 圖一(普通佇列)

此時,數字1,2,3,4已經將佇列給佔用滿了,若要再put 數字5的話,需要將裡面的數字全部get出來,然後再put數字5進去,如下圖所示:

        圖二(普通佇列)

3.2.2、迴圈佇列

  同樣以int值1,2,3,4,5為例,分別進行put佇列操作如下圖所示:

        圖三(迴圈佇列)

  此時儲存1,2,3,4是與圖一的普通佇列沒有區別,區別在於當資料大於佇列長度本身的時候,

如下所示:

                   圖四(迴圈佇列)

  此時put數字5時與圖二不一樣,不需要將資料全部將資料取出,由於佇列先進先出的性質,

只需get出數字1,騰出一個空間,就可以將數字5 put 佇列之中了。

當然,以上的例子只是介紹什麼是佇列與迴圈佇列的儲存結構,

至於為什麼說迴圈佇列可以充分利用儲存空間,比普通佇列節省,請往下看。

3.3、線性迴圈佇列的優勢展現

  對於3.2,3.3的描述,有讀者會說,可以對佇列的儲存空間進行擴容,反正陣列的索引都是固定的,操作起來效率也是蠻快的,不影響啥的。

  但是無論我們將儲存空間擴充套件的有多麼的大,都是有限的,畢竟整個計算機都是有限的東西組裝成的。再者研究資料結構與演算法就是為了將空間與時間怎樣進行到最優化,以此來節省有限的資源(包含時間)。

所以在此假設佇列的長度為固定值4,不允許擴容的情況下,同樣以1,2,3,4,5數字為例,如下所示:

當講1,2,3,4數字分別依次put到佇列,並get出1,2,3數字的情況下,如下圖所示:

                 圖五(普通佇列)

此時,若我們這時需要將5 put到佇列時對於普通佇列來說只能進行擴容,但是這樣就造成對原來佇列空間(數字1,2,3所騰出來)的浪費。

 

                    圖六(迴圈佇列)

同樣的情況,我們的迴圈佇列可以在不擴容的情況下將數字5 put 到佇列中,從而避免空間的浪費。

3.4、鏈式佇列

鏈式佇列也可以分為普通鏈式佇列(單鏈表結構)和迴圈鏈式佇列(迴圈單鏈表結構)。

3.4.1、兩種方式的描述

此處是將含數字1,2,3,4,5的節點,依次以佇列的方式形成連結串列,如下所示。

                                    圖七(普通鏈式佇列)

與普通鏈式佇列不同的是尾節點(tail)永遠指向頭節點(header),而不是NULL。

                                                              圖八(迴圈鏈式佇列)

優勢對比,由於鏈式佇列是對連結串列的尾節點(tail)進行插入操作,頭節點(header)進行刪除處理,所以都是O(1)的時間效率,至於空間上,這兩者對於空間的佔用也幾乎沒有什麼區別,因此,整體上來說實現哪一種都差不多。

4、程式碼的實現

4.1、線性佇列的實現(迴圈佇列)

package com.queue;

/**
 * 用陣列實現佇列的操作(迴圈佇列)
 */
public class ArrayQueue {
    //佇列的最大長度
    private static final int SIZE = 10;

    //佇列的當前長度
    private int length;

    //當前的佇列頭的下標
    private int header;

    //佇列陣列
    private int queue[];

    /**
     * 初始化佇列陣列
     * 佇列的當前長度都為-1用來判斷佇列是否為空!
     * 當前的佇列頭的下標的下標為0
     */
    public ArrayQueue() {
        queue = new int[SIZE];
        length = -1;
        header = 0;
    }

    /**
     * 新增佇列資料
     * @param data 資料
     */
    public void addQueue(int data) throws Exception {

        if(length % SIZE == header) {
            throw new Exception("該佇列已滿!");
        }

        if(length == -1) {
            length = header;
        }

        //將資料放入佇列陣列中
        queue[length++] = data;

        if(length == SIZE) {
            length = 0;
        }
    }

    /**
     * 獲取佇列資料
     */
    public int getQueue() throws Exception {

        if(length == -1) {
            throw new Exception("該棧為已空!");
        }

        //將資料放入佇列陣列中
        int data = queue[header++];

        if(header == SIZE) {
            header = 0;
        }
        if(header % SIZE == length) {
            length = -1;
        }
        return data;
    }

    /**
     * 清空佇列
     */
    public void clearQueue() {
        length = -1;
    }


    /**
     * 判斷佇列是否為空!
     */
    public boolean isEmpty() {
        return length == -1? true : false;
    }

    /**
     * 判斷佇列是否已滿!
     */
    public boolean isFull() {
        return length % SIZE == header? true : false;
    }

    /**
     * 列印佇列
     */
    public void printQueue() {
        if(length == -1) {
            System.out.println("該佇列為空!");
        }

        for(int i = header; (i % SIZE)< length; i++) {
            System.out.print(queue[i] + " ");
        }
        System.out.println();
    }
}

4.2、鏈式佇列(也是迴圈列表為例)

4.2.1、連結串列節點的實現

package com.node;

/**
 * 單鏈表節點
 *
 */
public class Node {
    //資料
    public int data;

    //指向下一個節點的引用
    public Node next;

    /**
     * 初始化節點資料
     * @param data 資料
     */
    public Node(int data) {
        this.data = data;
    }
}

4.2.2、鏈式佇列的實現

package com.queue;

import com.node.Node;

/**
 * 使用鏈式實現佇列的操作(迴圈佇列)
 */
public class LinkQueue {

    //佇列的頭節點
    private Node header = null;

    //佇列的尾節點
    private Node tail = null;

    public void putQueue(Node node) {
        if(header == null) {//佇列不存在時(頭節點就是尾節點,對尾節點進行插入操作)
            header = node;
            tail = node;
            tail.next = header;
        } else {//佇列存在時(對尾節點進行插入操作)
            tail.next = node;
            tail = tail.next;
            tail.next = header;
        }
    }

    public int getQueue() throws Exception {
        if(header == null) {
            throw new Exception("該佇列為已空!");
        }

        int data = header.data;//取出頭節點的值
        if(header.next != header) {//刪除頭節點
            header = header.next;
            tail.next = header;
        } else {
            //若頭節點與尾節點同指向一個節點時,
            // 此時取出該節點的值,並將頭結點與尾節點置為空!
            header = null;
            tail = null;
        }
        return data;
    }

    /**
     * 清空佇列
     */
    public void clearQueue() {
        header = null;
        tail = null;
    }

    /**
     * 判斷佇列是否為空!
     */
    public boolean isEmpty() {
        return header == null? true : false;
    }
}

5、測試

5.1、線性佇列測試的程式碼實現

package com.test;

import com.queue.ArrayQueue;

/**
 * 線性迴圈佇列的測試
 */
public class ArrayQueueTest {

    public static void main(String[] args) throws Exception{
        ArrayQueue arrayQueue = new ArrayQueue();

        //put值
        for (int i = 0; i < 10; i++) {
            arrayQueue.addQueue(i);
        }

        //get值
        for (int i = 0; i < 10; i++) {
            System.out.print(arrayQueue.getQueue() + " ");
        }

        System.out.println();
    }
}

測試結果

5.2、鏈式佇列測試的程式碼實現

package com.test;

import com.node.Node;
import com.queue.LinkQueue;

/**
 * 鏈式迴圈佇列的測試
 */
public class LinkQueueTest {

    public static void main(String[] args) throws Exception {

        LinkQueue linkQueue = new LinkQueue();

        //put含數字1,2的節點
        linkQueue.putQueue(new Node(1));
        linkQueue.putQueue(new Node(2));

        //get節點中的值
        System.out.println(linkQueue.getQueue());
        System.out.println(linkQueue.getQueue());
//        System.out.println(linkQueue.getQueue());
    }
}

測試結果

6、結束