一、什麼是隊列?
隊列是資料從表的一端進,另一端出,并遵循“先進先出” 原則的線性存儲結構。
圖一(隊列存儲結構)
通常,稱進資料的一端為 “隊尾”,出資料的一端為 “隊頭”,資料元素進隊列的過程稱為 “入隊”,出隊列的過程稱為 “出隊”。
不僅如此,隊列中資料的進出要遵循 “先進先出” 的原則,即最先進隊列的資料元素,同樣要最先出隊列。拿圖 1 中的隊列來說,從資料在隊列中的存儲狀态可以分析出,元素 1 最先進隊,其次是元素 2,最後是元素 3。此時如果将元素 3 出隊,根據隊列 “先進先出” 的特點,元素 1 要先出隊列,元素 2 再出隊列,最後才輪到元素 3 出隊列。
棧和隊列不要混淆,棧結構是一端封口,特點是"先進後出";而隊列的兩端全是開口,特點是"先進先出"。
二、隊列存儲實作
隊列存儲結構的實作有以下兩種方式:
1.順序隊列:在順序表的基礎上實作的隊列結構;
2.鍊隊列:在連結清單的基礎上實作的隊列結構;
(本文主要将順序存儲)
三、順序隊列簡單實作
由于順序隊列的底層使用的是數組,是以需預先申請一塊足夠大的記憶體空間初始化順序隊列。除此之外,為了滿足順序隊列中資料從隊尾進,隊頭出且先進先出的要求,我們還需要定義兩個指針(top 和 rear)分别用于指向順序隊列中的隊頭元素和隊尾元素,如圖 1 所示:
圖 1 順序隊列實作示意圖
由于順序隊列初始狀态沒有存儲任何元素,是以 top 指針和 rear 指針重合,且由于順序隊列底層實作靠的是數組,是以 top 和 rear 實際上是兩個變量,它的值分别是隊頭元素和隊尾元素所在數組位置的下标。
在圖 1 的基礎上,當有資料元素進隊列時,對應的實作操作是将其存儲在指針 rear 指向的數組位置,然後 rear+1;當需要隊頭元素出隊時,僅需做 top+1 操作。
例如,在圖 1 基礎上将 {1,2,3,4} 用順序隊列存儲的實作操作如圖 2 所示:
圖二 資料進順序隊列的過程實作示意圖
在圖 2 基礎上,順序隊列中資料出隊列的實作過程如圖 3 所示:
圖三 資料出順序隊列的過程示意圖
從以上我們可以大緻知道,入隊列時rear也就是隊尾會向後移,出隊列時top也就是隊頭向後移,當它們重合的時候,也就是隊列元素為空的時候。
四、JAVA自帶隊列類的使用
public class Team {
/*聲明一個queue對象,
* ArrayBlockingQueue :一個由數組支援的有界隊列。
* LinkedBlockingQueue :一個由連結節點支援的可選有界隊列。
* PriorityBlockingQueue :一個由優先級堆支援的無界優先級隊列。
* DelayQueue :一個由優先級堆支援的、基于時間的排程隊列。
* SynchronousQueue :一個利用 BlockingQueue 接口的簡單聚集(rendezvous)機制。*/
Queue queue=new ArrayBlockingQueue(20);
public Team(){
//入隊
queue.add(1);
queue.add(2);
queue.add(3);
//出隊
queue.poll();
System.out.println(queue);
}
public static void main(String[] args) {
new Team();
}
}
結果如圖:
其中隊列操作方法:
add 增加一個元索 如果隊列已滿,則抛出一個IIIegaISlabEepeplian異常
remove 移除并傳回隊列頭部的元素 如果隊列為空,則抛出一個NoSuchElementException異常
element 傳回隊列頭部的元素 如果隊列為空,則抛出一個NoSuchElementException異常
offer 添加一個元素并傳回true 如果隊列已滿,則傳回false
poll 移除并返問隊列頭部的元素 如果隊列為空,則傳回null
peek 傳回隊列頭部的元素 如果隊列為空,則傳回null
put 添加一個元素 如果隊列滿,則阻塞
take 移除并傳回隊列頭部的元素 如果隊列為空,則阻塞
添加删除時盡量使用offer,poll,用add和remove失敗抛異常
五、JAVA代碼實作隊列
package ddd;
public class Tesr {
//因為如圖中,隊列剛開始指向0是以此處給它們兩賦初值為0,正好也對應數組下标
int rear=0;
int top=0;
//數組
int arr[];
//隊列的長度
int size;
//給數組隊列賦容量
public void numArr(int num){
this.size=num;
arr=new int[num];
}
//判斷隊列是否為空
public boolean isEmpty(){
return rear==top;
}
//判斷隊列是否已滿
public boolean isFull(){
return rear==size-1;
}
//入隊
public void add(int value){
if(isFull()){
throw new RuntimeException("隊列已滿");
}
rear++;
arr[rear-1]=value;
System.out.println("存入的值為:"+value);
}
//出隊
public void poll(){
if(isEmpty()){
throw new RuntimeException("隊列已空");
}
top++;
//top++之後指向下一個元素,取出的元素是++之前的元素,是以top-1
System.out.println("取出的值為:"+arr[top-1]);
}
//周遊隊列
public void see(){
if(isEmpty()){
return ;
}
for (int i = 0; i <=rear-1; i++) {
System.out.println(arr[i]);
}
}
public static void main(String[] args) {
Tesr test=new Tesr();
test.numArr(20);
test.add(1);
test.add(2);
test.add(3);
test.poll();
test.poll();
test.poll();
test.poll();
test.see();
}
}
測試結果如圖:
以上是基于單向連結清單實作的,缺點在于:
指針 top 和 rear 重合位置指向了後續的重合點而不再是 a[0]。也就是說,整個順序隊列在資料不斷地進隊出隊過程中,在順序表中的位置不斷後移。
順序隊列整體後移造成的影響是:
- 順序隊列之前的數組存儲空間将無法再被使用,造成了空間浪費;
- 如果順序表申請的空間不足夠大,則直接造成程式中數組 a 溢出,産生溢出錯誤;
為了解決以上兩個問題,可以使用巧妙的方法将順序表打造成一個環狀表,如下圖所示:
隻是一個想象圖,在真正的實作時,沒必要真建立這樣一種結構,我們還是使用之前的順序表,也還是使用之前的程式,隻需要對其進行一點小小的改變:(具體改變下次更)
如有不足請指出