數據結構:環形隊列(Java)

一.什麼是環形隊列?

環形隊列是一種特殊的隊列結構,元素同樣也是先進先出的,但是與一般隊列的區別是,它們是環形的,即隊列頭部的上以個元素是隊列的尾部,通常是容納元素數固定的一個閉環。如圖:
環形隊列

二.環形隊列的優點

  • 操作快捷。所有隊列操作都需要 O(1)的 時間。
  • 元素空間可以重複利用:因爲環形隊列都是一個元素數固定的一個閉環,可以在環形隊列初始化的時候分配好固定的內存空間,當進隊或者出隊時只需要返回指定元素內存空間的地址即可,並且這些內存空間可以重複利用,避免頻繁內存分配和釋放的開銷。

三.隊列的應用

  • 打印機使用隊列來管理作業-作業按照提交順序進行打印。
  • Web服務器使用隊列來管理請求-頁面請求按照接收順序得到滿足。
  • 進程在CPU調度程序的隊列中等待運行。
  • 廣度優先搜索使用隊列來跟蹤接下來要訪問的節點。

四.環形隊列實現分析

1.首先來分析環形隊列爲滿的情況:

  • 環形隊列爲空: front == year。
  • 環形隊列爲滿:這裏隊列的容量要空出一個作爲約定,然後隊尾索引 rear 的下一位爲頭索引 front 時表示隊列滿,即:(rear + 1) % maxSize == front。(因爲環形隊列操作倆指針都會 ++,所以用取模的方法防止隊頭隊尾指針越界)
    注意: 在環形隊列中,由於入隊時尾指針向前追趕頭指針;出隊時頭指針向前追趕尾指針,造成隊空和隊滿時頭尾指針均相等。因此,無法通度過條件front==rear來判別隊列是"空"還是"滿",所以環形隊列要空出一個作爲約定。
    下面展示代碼示例
// 判斷隊列是否已滿
    public boolean isFullCircleQueue() {
        return (rear + 1) % maxSize == front;
    }
// 判斷隊列是否爲空
    public boolean isEmptyCircleQueue() {
        return front == rear;
    }

2.環形隊列入隊出隊

  • 入隊分析:添加數據到數組,將 rear 後移,這裏也要用取模的方法:rear = (rear + 1) % maxSize。
  • 下面展示 代碼示例
// 添加數據到隊列
    public void addCircleQueue(int value) {
        if (isFullCircleQueue()) {
            System.out.println("隊列已經滿了,不能繼續添加數據");
            return;
        }
        // rear 默認爲 0,直接添加數據到數組
        arr[rear] = value;
        // 將 rear後移,但是要防止越界,這裏考慮用 %
        rear = (rear + 1) % maxSize;
    }
  • 出隊分析:環形隊列的出隊不能直接 front + 1,因爲 front 是始終指向隊頭的;用一個臨時變量存儲,在將 front 後移,同入隊操作。
  • 下面展示 代碼示例
// 數據出隊
    public int getCircleQueue() {
        // 判斷隊列是否爲空
        if (isEmptyCircleQueue()) {
            // 拋出異常
            throw new RuntimeException("隊列爲空,不能獲取數據喔");
        }
        // front 始終指向隊列的第一個元素
        // 先用一個臨時變量存儲當前隊列元素
        // front後移 指向下一個隊列元素,同樣考慮到防止越界
        int temp = arr[front];
        front = (front + 1) % maxSize;
        return temp;

3.環形隊列的遍歷

  • 遍歷分析:隊列容量 maxSize 是固定的,但是隊列的有效數據元素卻是不一定的。例如:maxSize = 5, 但是隊列元素只有 : 100, 99, 100 這三個,所以就不能用單向隊列的遍歷方式。
  • 獲取有效數據元素大小:(當前隊列的隊尾下標 — 當前隊列的隊首下標 + 隊列容量)% 隊列容量,即:(rear - front + maxSize) % maxSize
  • 下面展示 代碼示例
// 獲取有效數據的長度
    public int effectDataSize() {
        return (rear - front + maxSize) % maxSize;
    }

五.完整代碼

下面展示 完整代碼

import java.util.Queue;
import java.util.Scanner;
import javax.management.RuntimeOperationsException;

public class CircleQueueDemo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        CircleQueue circleQueue = new CircleQueue(5); // 隊列長度設置 5
        char input = ' '; // 用戶輸入
        boolean tag = true;
        // 菜單
        while (tag) {
            System.out.println("S(showCircleQueue): 打印環形隊列");
            System.out.println("A(addCircleQueue): 添加數據-入隊");
            System.out.println("G(getCircleQueue): 取出數據-出隊");
            System.out.println("H(showHeadCircleFront): 查看隊頭的數據元素");
            System.out.println("E(exit): 退出程序");
            // 獲取用戶輸入的字符
            input = scanner.next().charAt(0);

            switch (input) {
                case 'S':
                    circleQueue.showCircleQueue();
                    break;
                case 'A':
                    System.out.println("輸入一個數");
                    int value = scanner.nextInt();
                    circleQueue.addCircleQueue(value);
                    break;
                case 'G':
                    try {
                        int res = circleQueue.getCircleQueue();
                        System.out.printf("取出的數據爲: %d\n", res);
                    } catch (Exception E) {
                        System.out.println(E.getMessage());
                    }
                    break;
                case 'H':
                    try {
                        int res = circleQueue.showHeadCirclQueue();
                        System.out.printf("隊列頭的數據是: %d\n", res);
                    } catch (Exception E) {
                        System.out.println(E.getMessage());
                    }
                    break;
                case 'E':
                    // 關閉
                    scanner.close();
                    tag = false;
                    break;
                default:
                    break;
            }
        }

        System.out.println("程序退出!");
    }
}

class CircleQueue {

    private int front; // 隊列頭部
    private int rear; // 隊列尾部
    private int maxSize; // 數組的最大容量
    private int[] arr; // 用數組模擬隊列

    public CircleQueue(int arrMaxSize) {
        this.maxSize = arrMaxSize;
        this.arr = new int[maxSize];
    }

    // 判斷隊列是否已滿
    public boolean isFullCircleQueue() {
        return (rear + 1) % maxSize == front;
    }

    // 判斷隊列是否爲空
    public boolean isEmptyCircleQueue() {
        return front == rear;
    }

    // 添加數據到隊列
    public void addCircleQueue(int value) {
        if (isFullCircleQueue()) {
            System.out.println("隊列已經滿了,不能繼續添加數據");
            return;
        }
        // rear 默認爲 0,直接添加數據到數組
        arr[rear] = value;
        // 將 rear後移,但是要防止越界,這裏考慮用 %
        rear = (rear + 1) % maxSize;
    }

    // 數據出隊
    public int getCircleQueue() {
        // 判斷隊列是否爲空
        if (isEmptyCircleQueue()) {
            // 拋出異常
            throw new RuntimeException("隊列爲空,不能獲取數據喔");
        }
        // front 始終指向隊列的第一個元素
        // 先用一個臨時變量存儲當前隊列元素
        // front後移 指向下一個隊列元素,同樣考慮到防止越界
        int temp = arr[front];
        front = (front + 1) % maxSize;
        return temp;
    }

    // 遍歷環形隊列
    public void showCircleQueue() {
        // 判斷是否爲空
        if (isEmptyCircleQueue()) {
            System.out.println("隊列爲空,不能獲取數據");
            return;
        }

        for (int i = front; i < front + effectDataSize(); i++) {
            System.out.printf("arr[%d] = %d\n", i % maxSize, arr[i % maxSize]);
        }
    }

    // 獲取有效數據的長度
    public int effectDataSize() {
        return (rear - front + maxSize) % maxSize;
    }

    public int showHeadCirclQueue() {
        // 先判斷隊列是否爲空
        if (isEmptyCircleQueue()) {
            // 拋出異常
            throw new RuntimeException("隊列爲空,不能獲取數據");
        }
        return arr[front];
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章