我所知道的數據結構之隊列

做者前言


你們好,我是阿濠,今篇內容跟你們分享的是數據結構之隊列,很高興分享到segmentfault與你們一塊兒學習交流,初次見面請你們多多關照,一塊兒學習進步.

1、什麼是消息隊列


官方定義:隊列(quene)是容許在一端進行插入操做,而在另外一端進行刪除操做的線性表

1.隊列是一個有序列表,能夠用數組或是鏈表來實現。
2.遵循先入先出的原則。即:先存入隊列的數據,要先取出。後存入的要後取出算法

好比春運時的標語排隊走得了、走的及時、走得安全、走的溫馨segmentfault

圖片.png

優先排隊的人, 優先處理. (買票, 結帳, WC) 後端

隊列的插入和刪除

對於棧來講,只須要一個棧頂指針就能夠了。
可是對於隊列來說,須要兩個指針:一個是head指針,指向隊頭,一個tail指針,指向隊尾。數組

這裏圖中初始化無數據的時候隊首和隊尾都爲-1,
當有數據增長的時候,隊尾在變化,而隊首沒有變化,說明是隊首添加數據安全

當有數據取出的時候,隊首在變化,而隊尾沒有變化,說明是隊尾去除數據數據結構

2、消息隊列實現方式

數組模擬隊列

隊列自己是有序列表,若使用數組的結構來存儲隊列的數據,則隊列數組的聲明以下圖,其中maxSize是該隊列的最大容量。學習

➢由於隊列的輸出、輸入是分別從先後端來處理,所以須要兩個變量front及rear分別記錄隊列先後端的下標測試

使用front記錄隊首、使用rear記錄隊尾優化

front 會隨着數據輸出而改變表明取出數據,而rear則是隨着數據輸入而改變表明添加數據spa

如圖所示:
圖片.png

當咱們將數據存入隊列時稱爲"addQueue",思路分析以下:
1.判斷隊列是否滿或是否爲空,而後纔將隊尾指針rear日後移:rear+1
2.隊尾指針rear小於數組長度-1能夠添加,若隊尾指針rear==maxSize-1則表明隊列已滿

編寫代碼以下:

//使用數組模擬隊列-編寫ArrayQueue類
public class ArrayQueue {

    private int maxSize; //表示數組的最大容量
    private int front; //隊列頭
    private int rear; //隊列尾
    private int[] arr; //該數據用於存放數據,模擬隊列

    //建立隊列的構造器
    public ArrayQueue(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[maxSize];//初始化數組
        front = -1; //指向隊列頭部,即返回即第一個數據的前一個位置
        rear = -1;//指向隊列尾部,即返回隊列尾的數據(即包含隊列最後一個數據)
    }

    //判斷隊列是否已滿
    public boolean isFull(){
        return rear==maxSize-1;
    }
    //判斷隊列是否爲空
    public boolean isEmpty(){
        return rear==front;
    }
    //添加數據到隊列
    public void addQueue(int n){
        //判斷隊列是否已滿,已滿不可添加
        if(isFull()){
            System.out.println("隊列已滿,沒法添加數據");
            return ;
        }
        //後移並添加數據
        arr[++rear]=n;
    }
    //從隊列取出數據
    public int getQueue(){
        if(isEmpty()){
            throw new RuntimeException("隊列爲空,不能取出數據");
            //無需在return  由於throw有帶return
        }
        //取出數據後,隊頭須要日後移,而後返回數據
        return arr[++front];
    }
    //打印隊列裏的數據
    public  void showQueue() {
        if (isEmpty()) {
            System.out.println("隊列爲空,沒法打印數據");
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.printf("arr[%d]=%d\n", i, arr[i]);
        }
    }
    //打印隊列頭部數據
    public int showheadQueue(){
        //判斷隊列是否爲空
        if (isEmpty()) {
            throw new RuntimeException("隊列爲空,沒有數據");
        }
        return arr[front+1];
    }
}

如今添加數據完成方法測試看看隊列

//建立一個隊列
ArrayQueue arrayQueue = new ArrayQueue(3);

System.out.println("打印隊列數據----------------");
arrayQueue.showQueue();//打印隊列數據

System.out.println("\n添加數據:10");
arrayQueue.addQueue(10);//添加一個數據10

System.out.println("\n打印隊列數據----------------");
arrayQueue.showQueue();//打印隊列數據

//打印頭部數據
System.out.println("\n\n顯示隊列隊首信息:"+arrayQueue.showheadQueue());

運行結果以下:

打印隊列數據----------------
隊列爲空,沒法打印數據

添加數據:10

打印隊列數據----------------
arr[0]=10    arr[1]=0    arr[2]=0    

顯示隊列隊首信息:10

如今數據添加成功、打印數據添加成功、輸出隊首數據成功,那麼繼續添加數據看看

System.out.println("\n添加數據:20");
arrayQueue.addQueue(20);//添加一個數據10

System.out.println("\n添加數據:30");
arrayQueue.addQueue(30);//添加一個數據10

System.out.println("\n添加數據:40");
arrayQueue.addQueue(40);//添加一個數據10

那麼打印看看會是什麼結果呢?

添加數據:20

添加數據:30

添加數據:40
隊列已滿,沒法添加數據

咱們發現隊列空間爲三,再次添加第四個數據的時候,就會出現隊列已滿沒法添加數據的異常

那麼咱們這樣使用數組模擬隊列,有沒有問題呢?

答案是有的,即數據取出後指向隊首的指針上去了,雖然以前的空間沒有數據了但沒法再次使用了。再次添加數據的時候顯示隊列已滿

問題分析並優化

1.目前數組使用一次就不能再次使用,沒有達到複用的效果
2.將數組使用算法,改爲環形數組模擬環形隊列,使用%

數組模擬環形隊列

如何將數組轉換成模擬循環隊列呢?

思路分析以下:

1.front指針做用進行調整:front指向數組第一個元素,初始值front=0

2.rear指針做用進行調整:rear指向隊列的最後一個元素後一個元素,初始值rear=0

原先rear指向的是最後一個元素,如今指向最後一個元素後一個元素爲空作約定,這樣隊列滿了以後隊首數據front前面元素就爲空了,在前一個元素就是隊列裏的最後一個元素了

圖片.png

3.當隊列滿時,條件是:(rear+1)%maxSize==front

原先線性數組判斷滿的條件爲rear=maxSize-1,沒有考慮複用,即如今是一個環,那麼此時的條件=(rear+1)%maxSize==front

圖片.png

4.當隊列爲空的條件:rear==front

5.隊列中有效的個數:(rear+maxSize-front)%maxSize

圖片.png

編寫代碼以下:

public class CircleArray {

    private int maxSize; //表示數組的最大容量
    //front 就指向隊列的第一個元素,也就是說arr[ front]
    //front的初始值= 0
    private int front;
    //rear指向隊列的最後一個元素的後一個位置。由於但願空出一個位置作約定區分隊列滿沒滿
    //rear的初始值= 0
    private int rear; //隊列尾.
    private int[] arr; //該數據用於存放數據,模擬隊列

    public CircleArray(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[maxSize];
    }
    //判斷隊列是否滿
    public boolean isFu1l () {
        return (rear + 1) % maxSize == front;
    }
    //判斷隊列是否爲空
    public boolean isEmpty(){
        return rear==front;
    }
    //添加數據到隊列
    public void addQueue(int n) {
    //判斷隊列是否滿
        if (isFu1l()) {
            System.out.println("隊列滿,不能加入數據~");
            return;
        }
        //由於rear自己指向最後一個元素的後一個位置
        //因此直接賦值,在將rear後移
        arr[rear] = n;
        //rear日後移須要% 即便回到前面有空餘的空間位置
        //由於環形隊列是一個環,不能像線性數組直接++ 會形成數組越界
        rear=(rear+1)%maxSize;
        //好比說有模擬環形隊列數組長度5分別爲2,6,7,8
        //即下標4是rear指向最後一個元素的後一個位置
        //若是取出隊列數據2,front=1 那麼目前的隊列數據便是  6,7,8
        //這時rear要回到下標0,不能是++等於下標4 ,因此取% 回到0
    }
    //從隊列取出數據
    public int getQueue(){
        if(isEmpty()){
            throw new RuntimeException("隊列爲空,不能取出數據");
            //無需在return  由於throw有帶return
        }
        //front的含義:指向隊列的第一個元素
        //1.先把front對應的值保存到臨時變量
        //2.將front後移,指向新的隊列第一個元素,考慮取%
        //3.將臨時的變量彈出返回
        int value=arr[front];
        front=(front+1)%maxSize;
        return value;
        //好比說有模擬環形隊列數組長度5分別爲2,6,7,8
        //若是取出隊列數據2,那麼此時front須要日後移指向新的首數據
        //可是front不能++,由於若是++的話,到了最後一個元素是下標3
        //彈出後++front=4,再添加數據時,rear=下標0,而front是4會爆出異常因此須要取%
    }

    //打印隊列裏的數據
    public  void showQueue() {
        //判斷隊列是否爲空
        if (isEmpty()) {
            System.out.println("隊列爲空,沒法打印數據");
            return;
        }
        //不能像線性數組直接循環打印出數據,由於是數組模擬環形隊列
        //思路:從front開始遍歷,遍歷到front+隊列有效個數
        //循環隊列從front開始到front+size(),爲何要+front?不能直接小於size()?
        //由於好比說循環隊:3,5,6 初始化數據front=0 rear=3
        //那麼取出數據3 此時front=1 rear=3 這時打印數據若是是直接小於size()
        //則指打印出1,2 沒法打印出真正個數
        for (int i =front; i <front+size(); i++) {
            //爲何要i%maxSize  由於是環形,好比說3,5,6 初始化數據front=0 rear=3
            //此時取出數據3,5,此時front=2 rear=3,即添加數據4,1 此時front=2 rear=1
            //若i=2 此時i<5    5=2+(1 + 4 - 2) % 4;
            //若i不進行i%maxSize 則會在i++時沒法回到環形頭部 會++到front=5 數組越界爆出異常
            //因此須要i%maxSize回到環形頭部
            System.out.printf("arr[%d]=%d\t", i % maxSize, arr[i % maxSize]);
        }
    }

    //求出當前隊列有效數據的個數
    public int size() {
        //好比說環形隊列空間=4 值=3,5,6
        //初始值front=0 rear=4
        //此時隊列有效數據(4+3-0)%4=3 表明有效個數=3
        return (rear + maxSize - front) % maxSize;
    }
    //打印隊列頭部數據
    public int showheadQueue(){
        //判斷隊列是否爲空
        if (isEmpty()) {
            throw new RuntimeException("隊列爲空,沒有數據");
        }
        //直接返回front,由於front指向隊列第一個元素
        return arr[front];
    }
}

那麼如今讓咱們添加數據測試看看把

//建立一個隊列
CircleArray arrayQueue = new CircleArray(4);

System.out.println("\n添加數據:10");
arrayQueue.addQueue(10);//添加一個數據10

System.out.println("\n打印隊列數據----------------");
arrayQueue.showQueue();

運行結果以下:

添加數據:10

打印隊列數據----------------
arr[0]=10

咱們再加多點數據看看,並看看數組模擬環形隊列有什麼不同?

System.out.println("\n添加數據:10");
arrayQueue.addQueue(10);//添加一個數據10

System.out.println("\n添加數據:20");
arrayQueue.addQueue(20);//添加一個數據20

System.out.println("\n添加數據:30");
arrayQueue.addQueue(30);//添加一個數據30

//打印頭部數據
System.out.println("\n\n顯示隊列隊首信息:"+arrayQueue.showheadQueue());

System.out.println("\n打印隊列數據----------------");
arrayQueue.showQueue();

運行結果以下:
顯示隊列隊首信息:10

打印隊列數據----------------
arr[0]=10    arr[1]=20    arr[2]=30

此時咱們彈出隊首再看看?

System.out.println("\n取出隊列首----------------");
arrayQueue.getQueue();

System.out.println("\n打印隊列數據----------------");
arrayQueue.showQueue();

運行結果以下:
取出隊列首----------------

打印隊列數據----------------
arr[1]=20    arr[2]=30

此時咱們能添加數據嘛?答案是能夠的
它會加到哪呢?答案是:會加到下標3的位置,此時下標0就做爲約定位置,由於這個約定的位置是會動態變化的!!!

System.out.println("\n添加數據:50");
arrayQueue.addQueue(50);//添加一個數據50

System.out.println("\n打印隊列數據----------------");
arrayQueue.showQueue();

運行結果以下:
添加數據:50

打印隊列數據----------------
arr[1]=20    arr[2]=30    arr[3]=50

那麼此時的數組便可反覆利用,測試完成

相關文章
相關標籤/搜索