Java成神之路:第三帖----資料結構與演算法之佇列

小秋呀 發表於 2020-09-15

資料結構與演算法--佇列

今天掉了兩根頭髮,摸掉的,記得 別亂摸,很珍貴的!!

什麼是佇列?

1)佇列是一個有序列表,可以用陣列或是連結串列來實現

2)遵循 先入先出 的原則。即:先存入佇列的資料,要先取出。後存入的要後取出

3)示意圖:(使用陣列模擬佇列示意圖)

佇列

rear表示的是佇列尾,front表示的是佇列首,front值等於-1,表示佇列資料首的前一位(用陣列模擬佇列,陣列的下標第一個為0,用-1表示資料首的前一位),rear的值為-1,在這裡我認為是為了和front保持一致,這樣rear == front,表示陣列為空!


用陣列模擬佇列的思路

佇列本身是有序列表,若使用陣列的結構來儲存佇列的資料,則佇列陣列的宣告如下圖,其中 max Size是該佇列的最大容量,因為佇列的輸出、輸入是分別從前後端來處理,因此需要兩個變數 front及rear分別記錄佇列前後端的下標,front會隨著資料輸出而改變,而rear則是隨著資料輸入而改變,還是這個圖嘻嘻~

佇列

** 當我們將資料存入佇列時稱為”addQueue”,addQueue的處理需要有兩個步驟:

  1. 將尾指標往後移:rear+1,當 front == rear 佇列表示為空 (PS:陣列頭和陣列尾在一起,能不空嗎)
  2. 若尾指標rear小於佇列的最大下標 maxSize-1,則將資料存入rear所指的陣列元素中,否則無法存入資料reaI == maxSize-1 此時表示佇列滿
  3. 話不多說,上程式碼:**
package com.yishuai.queue;

public class ArrayQueueDemo {
    public static void main(String[] args) {
        //資料測試
        ArrayQueue arrayQueue = new ArrayQueue(3);
    }
}

class ArrayQueue {
    private int maxSize;//最大容量
    private int front;//佇列頭
    private int rear;//佇列尾
    private int[] arr;//存放資料的陣列,模擬佇列

    //佇列初始化
    public ArrayQueue(int maxSize) {
        this.maxSize = maxSize;
        arr = new int[maxSize];
        front = -1;//指向佇列的首部,分析出front指向佇列頭的前一個位置
        rear = -1;//指向佇列的尾部,指向佇列尾部的最後一個資料
    }

    //判斷佇列是否滿
    public boolean isFull() {
        return rear == maxSize - 1;
    }

    //判斷佇列是否空
    public boolean isEmpty() {
        return rear == front;
    }

    //將資料新增到佇列
    public void addQueue(int data) {
        //如果佇列已經滿了,就不能再新增資料進入
        if (isFull()) {
            System.out.println("佇列已經滿了");
            return;
        }
        rear++; //資料增加,佇列尾的下標增加,佇列頭不變
        arr[rear] = data;
    }

    //將資料從佇列中取出
    public int getQueue() {
        if (isEmpty()) {
            throw new RuntimeException("佇列為空,沒有資料可取");
        }
        front++;
        return arr[front];
    }

    //展示出佇列中的所有資料
    public void showQueue() {
        if (isEmpty()) {
            System.out.println("佇列為空,沒有資料哦~");
        }
        for (int data : arr) {
            System.out.print(data + "\t");
        }
    }
}

新的問題

因為此時的佇列,採取的是一個鏈式的,也就意味著,當前面的資料被取出的時候,即使此時佇列已經不是滿的了,但是佇列尾還是和maxSize - 1一樣大,資料在插入的時候,仍然還是會顯示:佇列已經滿啦~,此時我們如果想要解決這個問題,我們要使用: 環形佇列

迴圈佇列

有兩種方法(坑了我幾個小時):

第一種:

宣告:font為頭部第一個資料,rear為尾部最後一個資料的下一個位置

  1. font == rear的時候,佇列為空(這個沒問題吧?)
  2. 初始:font == rear == 0,此時佇列為空,當資料進入的時候,rear開始往後走,也就是+1
  3. 假如maxSize=5,放入四個資料,也就是rear往後走了四步之後,rear=4了,陣列有五個位置,但是裡面只有四個資料,最後一個空的資料還沒有放入,此時,假如rear再往前走一步,rear=5,但是陣列的下標是從0開始的,也就是說,(0,1,2,3,4)這裡就是五個單元格,到頂了
  4. 所以rear到5的時候,只能往回走,也就是說,rear=4之後,不是rear=5,而是rear=0,此時font也等於0,font == rear了,刺激吧?
  5. 前面說font == rear時,佇列為空,但是現在說font == rear時,佇列又是為滿的,哪裡出錯了嗎?大部分人是不是和我一樣的思想(也可能是我自戀),你沒錯,兩個都是對的,所以呢,單純的依靠font和rear已經不能滿足判斷的條件了,我的方法是再加入一個count計數,從0開始,加一個資料,count++,取一個資料,count-- 。count會在【0,5】之間變動,當font == rearcount=0時,佇列為空,當font == rearcount=5時,佇列為滿,至於有效資料的個數,直接看count的值即可,這是第一種方法(我感覺比第二種好)!!

第二種(我和室友討論了一個多小時,吐槽吐槽)

PS:重點來了!!!

這種方法有一個單元格是空的!!!假如最大空間為5,只能放入4個資料,還有一個空間,留著種水稻(粗口不斷!),竟然不把空間用完,這也太浪費了,我看了3遍,想著應該不會這麼傻吧,空間都不用完,家裡有礦啊,還以為是自己哪裡沒弄懂,搞了一遍又一遍,和室友討論了很久,也沒得出結論,最後翻書解決了(不賣書,別私聊找我問哈),重複一遍,有一個空間是空的!!一直都是!!

這種方法,意思是:
宣告:font為頭部第一個資料,rear為尾部最後一個資料的下一個位置

  1. font == rear的時候,佇列為空(這個沒問題吧?沒問題吧?)
  2. 假如maxSize = 5,什麼時候滿了呢?當rear跑到4的時候就滿了,也就是rear == 4,裡面就四個資料,還有一個空間呢,重複:種水稻了(空的),所以(rear+1)% maxSize == font時,就滿了(%是取餘數,也叫取模,/是取商),rear+1什麼意思?補上種水稻的那個空,然後再對maxSize取模,為什麼取模?因為可能正好是和font同一圈,也有可能已經和font相差一圈了,所以要做取模操作。
  3. 那麼有多少有效資料呢?可以直接看佇列的長度,也就是 |rear-font|(取絕對值,問我為什麼的先去面壁,再評論我給你回覆),所以就可以寫成這樣(rear+maxSize-font)%maxSize,
    更新結束了,我頭髮又掉了兩根,啊啊啊!!!

不點個贊再走,你對得起我頭髮嗎,對得起嗎?