聲明:碼字不易,轉載請註明出處,歡迎文章下方討論交流。
前言:Java數據結構與算法專題會不定時更新,歡迎各位讀者監督。本文介紹數據結構中的隊列(queue)的概念、存儲結構、隊列的特色,文末給出java實現循環隊列的代碼實現供讀者參考學習。java
隊列正如其名,隊列就像一支隊伍,有隊首(head)
和隊尾(tail)
以及隊列長度。隊列和棧相似,也是一個遵循特殊規則約束的數據結構。在上一篇文章java數據結構與算法——棧中介紹棧是一個後進先出
(LIFO)的數據結構,隊列正好與之相反,是一個先進先出(FIFO,First In First Out),例如咱們去肯德基排隊,先排上隊的確定先拿到餐出隊,這和咱們對列認知是一致的。算法
上面說到隊列是一個遵循特殊規則的數據結構,除了先進先出,隊列的插入只能從隊列的一端操做,咱們稱這端爲隊尾;對應的,移除只能從另外一端出來,咱們稱之爲隊首。segmentfault
將沒有元素的隊列稱之爲空隊,往隊列中插入元素的過程稱之爲入隊,從隊列中移除元素的過程稱之爲出隊。數組
通常而言,隊列的實現有兩種方式:數組實現和鏈表實現,本篇中採起數組實現,鏈表實如今後續補充。用數組實現的隊列有兩種:一種是順序隊列,另外一種是循環隊列,這兩種隊列的存儲結構和特色下文會逐一介紹。數據結構
說明:用數組實現隊列,若隊列中出現隊滿的狀況(由於在聲明隊列時,通常會指定一個初始容量),此時若是有新元素入隊,但沒有位置怎麼辦?要麼丟棄,要麼等待。學習
如下采用數組實現,初始化一個隊列長度爲6,隊列有兩個標記,一個隊頭的位置head,一個隊尾的位置tail,初始都指向數組下標爲0的位置,如圖所示:測試
在插入元素時,tail標記+1,好比入隊三個元素,依次爲A,B,C(注意是有順序的),則當前隊列存儲狀況如圖:
當前head爲0,tail爲3,接下來進行出隊操做,此處將A元素出隊,則head+1,此時隊列的存儲狀況如圖:this
根據上面的圖例,咱們能夠經過tail-head得到隊列中元素的個數。當tail==head時,此時隊列爲空,而當tail等於數組長度時,此時將沒法出入元素,那麼隊列是否已經滿呢?未必!由於head和tail在入隊和出隊操做中只增不減,所以head和tail都會最終指向隊列以外的存儲位置,此時雖然數組爲空,但也沒法將元素入隊。spa
上溢: 當隊列中沒法插入元素時,稱之爲上溢;
假上溢: 在順序隊列中,數組還有空間(不必定全空)但沒法入隊稱之爲假上溢;
真上溢: 若是head爲0,tail指向數組以外,即數組真滿了,稱之爲真上溢;
下溢: 若是空隊中執行出隊操做,此時隊列中無元素,稱之爲下溢code
如何解決「假上溢」的問題呢?此時引入循環隊列
。出現假上溢時,此時數組還有空閒的位置,將tail重新指向數組的0索引處便可,如圖所示:
若是繼續入隊E和F,則隊列的存儲結構如圖:
一般而言,在對head和tail加1時,爲了方便採用對數組長度取餘操做。另外因爲順序隊列存在「假上溢」的現象,因此通常用循環隊列實現。
在採用循環隊列實現的過程當中,當隊列滿隊時,tail等於head,而當隊列空時,tail也等於head,爲了區分兩種狀態,通常規定循環隊列的長度爲數組長度-1,即有一個位置不放元素,此時head==tail時爲空隊,而當head==(tail+1)%數組長度,說明對滿。
下面是循環隊列的java代碼,採用數組方式實現:
public class ArrayQueue { private final Object[] queue; //聲明一個數組 private int head; private int tail; /** * 初始化隊列 * @param capacity 隊列長度 */ public ArrayQueue(int capacity){ this.queue = new Object[capacity]; } /** * 入隊 * @param o 入隊元素 * @return 入隊成功與否 */ public boolean put(Object o){ if(head == (tail+1)%queue.length){ //說明隊滿 return false; } queue[tail] = o; tail = (tail+1)%queue.length; //tail標記後移一位 return true; } /** * 返回隊首元素,但不出隊 * @return */ public Object peak() { if(head==tail){ //隊空 return null; } return queue[head]; } /** * 出隊 * @return 出隊元素 */ public Object pull(){ if(head==tail){ return null; } Object item = queue[head]; queue[head] = null; return item; } /** * 判斷是否爲空 * @return */ public boolean isEmpty(){ return head == tail; } /** * 判斷是否爲滿 * @return */ public boolean isFull(){ return head == (tail+1)%queue.length; } /** * 獲取隊列中的元素個數 * @return */ public int getsize(){ if(tail>=head){ return tail-head; }else{ return (tail+queue.length)-head; } } }
下面是隊列的測試代碼
public class ArrayQueueTest { public static void main(String[] args) { ArrayQueue q = new ArrayQueue(4); //初始化隊列長度爲3,由於循環隊列有一個不放元素 System.out.println(q.put("張三")); //入隊,true System.out.println(q.put("李四")); //入隊,true System.out.println(q.put("趙五")); //入隊,true System.out.println(q.put("老王")); //隊滿,false System.out.println(q.isFull()); //隊滿 true System.out.println(q.getsize()); //3,隊列中有3個元素 System.out.println(q.peak()); //返回「張三」 不出隊 System.out.println(q.pull()); //返回「張三」 System.out.println(q.pull()); //返回「李四」 System.out.println(q.pull()); //返回「趙五」 System.out.println(q.isEmpty()); //true 空隊 } }
隊列先入先出
的特色,使得其應用很是普遍,好比隊列做爲「緩衝區」,能夠解決計算機和外設速度不匹配的問題,FIFO的特色保證了數據傳輸的順序;除此以外隊列在後面樹的層序遍歷中也有應用,FIFO的特色保證了處理順序不會出錯。