陣列模擬佇列進階版本——環形佇列(真正意義上的排隊)

王宇凡發表於2022-04-23

陣列模擬環形佇列(真正意義上的排隊)

昨天我們做了陣列模擬佇列的基本情景。可以進行排隊和取出資料(最早的人離開佇列),但是我們發現,取出的地方不能重複利用。讓我們的佇列成為了一次性佇列。今天我們來看如何將我們的佇列改進稱陣列模擬環形佇列。實現已釋放位置的重複利用

基本原理:

要知道我們實現佇列的基本是頭和尾。rear和front。這兩個的指向決定了佇列的頭尾。也就是佇列本身。這個具體指向頭部本身索引或者前一個後一個不是固定的。是根據具體演算法而定的。這次我們規定頭和尾預設指向0索引。

對前面的陣列模擬佇列的優化,充分利用陣列. 因此將陣列看做是一個環形的。(通過取模的方式式來實現即可)

分析說明:

1:尾索引的下一個為頭索引時表示佇列滿,即將佇列容量空出一個作為約定,這個在做判斷佇列滿的時候需要注意 (rear + 1) % maxSize == front 滿]

2:rear == front [空]

程式碼如下:

package com.joseph.sparseArray;

import java.util.Map;
import java.util.Scanner;

public class CircularQueue {
    public static void main(String[] args) {

            CQueue cQueue = new CQueue(5);
            Scanner sc = new Scanner(System.in);
        int i;
            while(true) {
                System.out.println("---------------排隊系統--------------");
                System.out.println("---------------1:排隊諮詢--------------");
                System.out.println("---------------2:結束排隊(最早成功的人)--------------");
                System.out.println("-------------- 3:檢視當前排隊詳情--------------");
                System.out.println("---------------88:SHOW REAR AND FRONT");
                System.out.println("---------------4:退出--------------");
                i = sc.nextInt();
                switch (i) {

                    case 1:
                        System.out.println("請輸入你的電話");
                        int tel = sc.nextInt();
                        cQueue.add(tel);
                        if(cQueue.rear == 0){
                            if(cQueue.arr[cQueue.MaxSize-1] == tel) {
                                System.out.println("排隊成功!");
                                System.out.println("已結束最早完成排隊的人。當前空閒位置為:" + ((cQueue.MaxSize - 1) - (cQueue.rear - cQueue.front + cQueue.MaxSize) % cQueue.MaxSize));
                            }
                        }else if (cQueue.arr[cQueue.rear - 1] == tel) {
                            System.out.println("排隊成功!");
                            System.out.println("已結束最早完成排隊的人。當前空閒位置為:" + ((cQueue.MaxSize -1) - (cQueue.rear - cQueue.front + cQueue.MaxSize) % cQueue.MaxSize));
                            break;
                        } else {
                            System.out.println("排隊失敗!");
                            System.out.println("已結束最早完成排隊的人。當前空閒位置為:" + ((cQueue.MaxSize -1) - (cQueue.rear - cQueue.front + cQueue.MaxSize) % cQueue.MaxSize));
                            break;
                        }
                    case 2:
                        int oldFront = cQueue.front;
                        cQueue.get();
                        if (cQueue.front != oldFront) {
                            System.out.println("已結束最早完成排隊的人。當前空閒位置為:" + ((cQueue.MaxSize -1) - (cQueue.rear - cQueue.front + cQueue.MaxSize) % cQueue.MaxSize));
                            break;
                        }
                        System.out.println("結束失敗");
                        break;
                    case 3:
                        cQueue.List();
                        break;
                    case 4:
                        System.out.println("謝謝使用!");
                        break;
                    case 88:
                        System.out.println("front:" + cQueue.front);
                        System.out.println("rear:"+cQueue.rear);
                }
            }
    }
}
class CQueue{
    int MaxSize ;//陣列最大容量,因為我們的REAR(尾部)指向的是最後一個資料索引的後一個。這也就是說我們的真實儲存長度要比MaxSize小一個。因為總要空出一個由rear指向。這是一個約定。前面有說到。
    int rear ;//尾部。指向佇列最後一個資料的後一個位置
    int front ;//頭部。直接指向第一個資料索引下標
    int arr[] ;//陣列
    public CQueue(int MaxSize){//基本沒變。rear和front初始值和上次不一樣。不懂得先看上次陣列模擬佇列基礎版
        this.MaxSize = MaxSize ;
        arr = new int[MaxSize];
        this.rear = 0;
        this.front = 0;
    }
    public boolean isFull(){
        return (rear+1)%MaxSize == front ;//由於環形佇列,其實佇列滿的狀態。是他們兩個差"一個",因為rear指向的是資料的後一個。而front指向的第一個資料。因為是環形,0和4要聯絡起來只差一個就需要取模。利用rear+1和MaxSize取模。等於front就代表滿了。這個需要好好理解。比較有難度
    }
    public boolean isEmpty(){//判斷為空。當他們兩個相等。不管在什麼位置。就代表沒有資料。
        return rear == front ;
    }

    public void add(int key){//新增資料
        if(isFull()){
            System.out.println("QUEUE IS FULL,CAN'T ADD ANYTHING");
            return ;
        }
        arr[rear] = key ;//直接將rear的下標賦值。
        rear = (rear+1)%MaxSize ;//這裡不能再自增了。因為是環形的。需要利用+1後取模實現週期性。否則會下標越界
    }
    public int get(){
        if(isEmpty()){
            throw new RuntimeException("QUEUE IS EMPTY,NO THINGS BE GETED");
        }
        int temp = arr[front] ;//這裡先把頭部資料拿出來。放到temp
        front = (front+1)%MaxSize ;//給front移位。當資料取出。就往後+1作為初始第一個資料。但是是環形的。繼續利用+1後取模實現週期性+1.不然會下標越界
        return temp ;
    }
    public void List(){
        //判斷
        if(isEmpty()){
            System.out.println("QUEUE IS EMPTY!");
            return ;
        }
        for(int i = front ; i < (rear - front + MaxSize)%MaxSize +front ;i++){//這裡不能用傳統思維去輸出了。要從front開始輸出。也就是佇列的開頭
            //而i的範圍。不再是佇列長度。而是佇列的有效資料個數+front。前面有算出是(rear-front+MaxSize)%MaxSize,其實rear是最後一個資料的後一個,而front就是第一個。我認為rear-front就是有效資料個數。而由於環形。往往出現負數。所以繼續取模達到絕對值的效果。由於我們是環形的。front初始位置會由於使用者取出導致變化。所以必須再加上front。因為front才是開始的位置。再加上有效資料個數。就是i的範圍
            System.out.printf("隊伍第[%d]號 : %d\n",i%MaxSize,arr[i%MaxSize]);//這裡輸出用printf方法做一個格式化。不能用i,要取模。否則會越界
        }
    }
}
/**
 * @author JosephWang
 * @date 2021/8/10 11:58
 */

相關文章