看動畫學算法之:隊列queue


簡介

隊列Queue是一個非常常見的數據結構,所謂隊列就是先進先出的序列結構。

想象一下我們日常的排隊買票,只能向隊尾插入數據,然后從隊頭取數據。在大型項目中常用的消息中間件就是一個隊列的非常好的實現。

隊列的實現

一個隊列需要一個enQueue入隊列操作和一個DeQueue操作,當然還可以有一些輔助操作,比如isEmpty判斷隊列是否為空,isFull判斷隊列是否滿員等等。

為了實現在隊列頭和隊列尾進行方便的操作,我們需要保存隊首和隊尾的標記。

先看一下動畫,直觀的感受一下隊列是怎么入隊和出隊的。

先看入隊:

再看出隊:

可以看到入隊是從隊尾入,而出隊是從隊首出。

隊列的數組實現

和棧一樣,隊列也有很多種實現方式,最基本的可以使用數組或者鏈表來實現。

先考慮一下使用數組來存儲數據的情況。

我們用head表示隊首的index,使用rear表示隊尾的index。

當隊尾不斷插入,隊首不斷取數據的情況下,很有可能出現下面的情況:

上面圖中,head的index已經是2了,rear已經到了數組的最后面,再往數組里面插數據應該怎么插入呢?

如果再往rear后面插入數據,head前面的兩個空間就浪費了。這時候需要我們使用循環數組。

循環數組怎么實現呢?只需要把數組的最后一個節點和數組的最前面的一個節點連接即可。

有同學又要問了。數組怎么變成循環數組呢?數組又不能像鏈表那樣前后連接。

不急,我們先考慮一個余數的概念,假如我們知道了數組的capacity,當要想數組插入數據的時候,我們還是照常的將rear+1,但是最后除以數組的capacity, 隊尾變到了隊首,也就間接的實現了循環數組。

看下java代碼是怎么實現的:

public class ArrayQueue {

    //存儲數據的數組
    private int[] array;
    //head索引
    private int head;
    //real索引
    private int rear;
    //數組容量
    private int capacity;

    public ArrayQueue (int capacity){
        this.capacity=capacity;
        this.head=-1;
        this.rear =-1;
        this.array= new int[capacity];
    }

    public boolean isEmpty(){
        return head == -1;
    }

    public boolean isFull(){
        return (rear +1)%capacity==head;
    }

    public int getQueueSize(){
        if(head == -1){
            return 0;
        }
        return (rear +1-head+capacity)%capacity;
    }

    //從尾部入隊列
    public void enQueue(int data){
        if(isFull()){
            System.out.println("Queue is full");
        }else{
            //從尾部插入
            rear = (rear +1)%capacity;
            array[rear]= data;
            //如果插入之前隊列為空,將head指向real
            if(head == -1 ){
                head = rear;
            }
        }
    }

    //從頭部取數據
    public int deQueue(){
        int data;
        if(isEmpty()){
            System.out.println("Queue is empty");
            return -1;
        }else{
            data= array[head];
            //如果只有一個元素,則重置head和real
            if(head == rear){
                head= -1;
                rear = -1;
            }else{
                head = (head+1)%capacity;
            }
            return data;
        }
    }
}

大家注意我們的enQueue和deQueue中使用的方法:

rear = (rear +1)%capacity
head = (head+1)%capacity

這兩個就是循環數組的實現。

隊列的動態數組實現

上面的實現其實有一個問題,數組的大小是寫死的,不能夠動態擴容。我們再實現一個能夠動態擴容的動態數組實現。

    //因為是循環數組,這里不能做簡單的數組拷貝
    private void extendQueue(){
        int newCapacity= capacity*2;
        int[] newArray= new int[newCapacity];
        //先全部拷貝
        System.arraycopy(array,0,newArray,0,array.length);
        //如果real<head,表示已經進行循環了,需要將0-head之間的數據置空,並將數據拷貝到新數組的相應位置
        if(rear< head){
            for(int i=0; i< head; i++){
                //重置0-head的數據
                newArray[i]= -1;
                //拷貝到新的位置
                newArray[i+capacity]=array[i];
            }
            //重置real的位置
            rear= rear+capacity;
            //重置capacity和array
            capacity=newCapacity;
            array=newArray;
        }
    }

需要注意的是,在進行數組擴展的時候,我們不能簡單的進行拷貝,因為是循環數組,可能出現rear在head后面的情況。這個時候我們需要對數組進行特殊處理。

其他部分是和普通數組實現基本一樣的。

隊列的鏈表實現

除了使用數組,我們還可以使用鏈表來實現隊列,只需要在頭部刪除和尾部添加即可。

看下java代碼實現:

public class LinkedListQueue {
    //head節點
    private Node headNode;
    //rear節點
    private Node rearNode;

    class Node {
        int data;
        Node next;
        //Node的構造函數
        Node(int d) {
            data = d;
        }
    }

    public boolean isEmpty(){
        return headNode==null;
    }

    public void enQueue(int data){
        Node newNode= new Node(data);
        //將rearNode的next指向新插入的節點
        if(rearNode !=null){
            rearNode.next=newNode;
        }
        rearNode=newNode;
        if(headNode == null){
            headNode=newNode;
        }
    }

    public int deQueue(){
        int data;
        if(isEmpty()){
            System.out.println("Queue is empty");
            return -1;
        }else{
            data=headNode.data;
            headNode=headNode.next;
        }
        return data;
    }
}

隊列的時間復雜度

上面的3種實現的enQueue和deQueue方法,基本上都可以立馬定位到要入隊列或者出隊列的位置,所以他們的時間復雜度是O(1)。

本文的代碼地址:

learn-algorithm

本文已收錄於 http://www.flydean.com/12-algorithm-queue/

最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!


免責聲明!

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



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