一.什麼是環形隊列?
環形隊列是一種特殊的隊列結構,元素同樣也是先進先出的,但是與一般隊列的區別是,它們是環形的,即隊列頭部的上以個元素是隊列的尾部,通常是容納元素數固定的一個閉環。如圖:
二.環形隊列的優點
- 操作快捷。所有隊列操作都需要 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];
}
}