什麼是隊列?
隊列的兩個基本操作是inserting(插入)一個數據項,即把一個數據項放入隊尾,另一個是removing(移除)一個數據項,即移除隊頭的數據項。這類似於電影愛好者排隊買票時先排到隊尾,然後到達隊頭買票後離開隊列。
棧中的插入和移除數據項方法的命名是很標準,稱爲push和pop。隊列的方法至今沒有標準化的命名。“插入”可以稱爲put、add或enque,而“刪除”可以叫delete、get或deque。插入數據項的隊尾,也可以叫作back、tail或end。而移除數據項的隊頭,也可以叫head。下面將使用insert、remove、front和rear。
插入將值插入隊尾,同時隊尾箭頭增加一,指向新的數據項。
數據項被移除後,同時隊頭指針增加一。通常實現隊列時,刪除的數據項還會保存在內存中,只是它不能被訪問了,因爲隊頭指針已經移到它的下一個位置了。
和棧中的情況不同,隊列中的數據項不總是從數組的0下標處開始。移除了一些數據項後,隊頭指針會指向一個較高的下標位置。
查看操作返回隊頭數據項的值,然而並不從隊中刪除這個數據項。
要是想從空隊列中移除一個數據項或想在已經滿的隊列中插入一個數據項,應用程序都要提示出錯消息。
上方左邊爲普通隊列,右圖爲環繞式隊列
循環隊列
當在隊列中插入一個新數據項,隊頭的Rear箭頭向上移動,移向數組下標大的位置。移除數據項時,隊尾Front指針也會向上移動。這種設計可能和人們直觀察覺相反,因爲人們在買電影票排隊時,隊伍總是向前移動的,當前面的人買完票離開隊伍後,其他人都向前移動。計算機中在隊列裏刪除一個數據項後,也可以將其他數據項都向前移動,但這樣做的效率很差。相反,我們通過隊列中隊頭和隊尾指針的移動保持所有數據項的位置不變。
這樣設計的問題是隊尾指針很快就會移到數組的末端。雖然在數組的開始部分有空的數據項單元,這是移除的數據項的位置,但是由於因爲隊尾指針不能再向後移動了,因此也不能再插入新的數據項,這該怎麼辦?
環繞式處理
爲了避免隊列不滿卻不能插入新數據項的問題,可以讓隊頭隊尾指針繞回到數組開始的位置。這就是循環隊列(有時也稱爲“緩衝環”)。
指針迴繞的過程:在隊列中插入足夠多的數據項,使隊尾指針指向數組的未端。再刪除幾個數組前端的數據項。現在插入一個新的數據項。就會看到隊尾指針從未端迴繞到開始處的位置。新的數據項將插入這個位置。
插入更多的數據項。隊尾指針如預計的那樣向上移動。注意在隊尾指針迴繞之後, 它現在處在隊頭指針的下面,這就顛倒了初始的位置。這可以稱爲“折斷的序列”:隊列中的數據項存在數組兩個不同的序列中。
刪除足夠多的數據項後,隊頭指針也迴繞。這時隊列的指針回到了初始運行時的位置狀態,隊頭指針在隊尾指針的下面。數據項也恢復爲單一的連續的序列。
下面我們實現一個循環處理隊列
public class Queue {
private int capacity;
private int[] intArray;
private int head;
private int tail;
private int length;
private static final int HEAD = 0;
private static final int TAIL = -1;
public Queue(int capacity){
this.capacity = capacity;
this.intArray = new int[capacity];
this.head = TAIL;
this.tail = TAIL;
this.length = HEAD;
}
public void add(int intValue) throws Exception {
//判斷對是否是滿的
if(isFull()){
throw new Exception("隊列滿了,別操作了");
}
if(tail == capacity -1){
tail = TAIL;
}
intArray[++tail] = intValue;
length++;
}
public int remove() throws Exception {
//判斷隊列是否是空的
if(isEmpty()){
throw new Exception("隊列是空的,別移除了");
}
int intElem = intArray[++head];
if(head == capacity){
head = 0;
}
//由於類型原因,把頭賦值成了0,實際賦值成null
intArray[head] = 0;
length --;
return intElem;
}
//隊列長度
public int getLength(){
return length;
}
//是否爲空
public Boolean isEmpty(){
return length == 0 ? true : false;
}
//是否滿了
public Boolean isFull(){
return length == capacity ? true : false;
}
//查看對頭元素
public int catHead() throws Exception {
if(isEmpty()){
throw new Exception("隊列是空的,你告訴我咋看");
}
return intArray[head];
}
public static void main(String[] args) throws Exception{
Queue queue = new Queue(10);
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);
queue.remove();
int i = queue.catHead();
System.out.println(i);
}
}
總結
1、該隊列爲非線程安全的,在多線程環境中可能會發生數據丟失等問題。
2、隊列通過移動指針來確定數組下標的位置,因爲是基於數組實現的,所以隊列的長度不能夠超過數組的長度。
3、該隊列是循環隊列,這就意味着數組可以重複被使用,避免了重複創建對象帶來的性能的開銷。
4、添加數據時總是從隊列尾部添加,拉取數據時總是從隊列頭部拉取,拉取完將對象元素刪除。