一、隊列的定義及特點
隊列是一種只允許在表的一端刪除(稱作表頭)和表的另一端加入(稱作表尾)的線性表。
從定義可以看出隊列的特點是先進先出。
二、順序表示的循環隊列
概括來說循環隊列是通過取模操作解決隊列空間假溢出的問題,能更充分的利用所分配空間。因爲在一般隊列的順序表示中,無論增加還是刪除,頭指針或尾指針都會遞加,這樣就會導致數組的低索引位置不可再被使用。
這種弊端在鏈式表示中不存在,因爲鏈式表示中並不是一次開闢所有空間,而是動態開闢,並且刪除的結點空間可以被釋放。
這裏只實現順序表示的循環隊列,個人認爲鏈表的循環沒有太大意義,如果只是循環而不增加隊列長度,那麼順序表示更方便,如果需要增加隊列長度的話,附帶頭尾指針的鏈表同樣可以替代。
詳細過程見代碼
public class CircularQueue {
private int[] arr;
private int maxSize;
private int head; // 隊頭出隊
private int tail; // 隊尾入隊
private int size; // 隊列中存在的元素個數
public CircularQueue(int size) { // 構造函數初始化相關信息
maxSize = size + 1; // 這裏+1是因爲下面選用的隊列判空條件會導致少用一個元素空間
arr = new int[maxSize];
head = tail = this.size = 0; // 初始頭尾指針都指0
}
private boolean isEmpty() { // 判斷隊空
return head == tail;
}
private boolean isFull() {
// 判斷隊滿,這種取模操作必定會導致減少一個可使用空間,並且注意這個不可用空間並不固定。
return (tail + 1) % maxSize == head;
}
public void inQueue(int num) {
if (isFull()) { // 入隊前判斷隊滿
System.out.println("隊滿!");
} else {
arr[tail] = num;
tail = (tail + 1) % maxSize;
size++;
}
}
public int outQueue() {
if (isEmpty()) { // 出隊前判斷隊空
System.out.println("隊空!");
return -1;
} else {
int num = arr[head];
head = (head + 1) % maxSize;
size--;
return num;
}
}
public int getSize() {
// 直接tail-head可能會出現負數,這是因爲循環過程中兩指針不斷移動,會出現tail<head的情況。
return (tail - head + maxSize) % maxSize;
}
}
在上面代碼中並沒有用到成員變量size的信息,size也可以用來判斷隊滿隊空且更加容易,但這就不是學習數據結構了~
三、鏈式表示的一般隊列
public class LinkedQueue {
private class Node { // 使用單鏈表存儲
int data;
Node next;
public Node() {
}
public Node(int num) {
data = num;
}
}
Node head; // 頭結點,指向隊列中的第一個結點
Node tail;
int size;
public LinkedQueue() {
head = new Node(); // 初始化,新建頭結點,尾指針指向頭結點
tail = head;
size = 0;
}
public void inQueue(int num) { // 入隊不需要判斷隊滿,入隊元素連接在隊尾,尾指針後移
Node node = new Node(num);
tail.next = node;
tail = node;
size++;
}
public int outQueue() { // 出隊需要判斷隊空
if (head == tail) {
System.out.println("隊空!");
return -1;
}
if (head.next == tail) { // 隊列中只有一個結點時斷開頭結點的引用
Node node = tail;
head.next = null;
tail = head;
size--;
return node.data;
} else { // 否則頭結點指針指向這個結點的下一個結點
Node node = head.next;
head.next = head.next.next;
size--;
return node.data;
}
}
public int getSize() {
return size;
}
}
隊列判空條件同樣可以使用成員變量size。鏈隊的刪除還應該注意要釋放結點空間,但因爲java中的GC在斷開結點所有引用之後會自動回收,所以省略了這一步。
補充:用兩個棧實現隊列
很簡單,具體代碼就不寫了,思路是一個棧用作入隊,一個棧用作出隊,當出隊棧空時將入隊棧中的所有元素依次彈出壓入到出隊棧中。