Java数据结构和算法(五)——队列
前面一篇博客我們講解了并不像數組一樣完全作為存儲數據功能,而是作為構思算法的輔助工具的數據結構——棧,本篇博客我們介紹另外一個這樣的工具——隊列。棧是后進先出,而隊列剛好相反,是先進先出。
回到頂部
1、隊列的基本概念
隊列(queue)是一種特殊的線性表,特殊之處在于它只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。隊列中沒有元素時,稱為空隊列。
隊列的數據元素又稱為隊列元素。在隊列中插入一個隊列元素稱為入隊,從隊列中刪除一個隊列元素稱為出隊。因為隊列只允許在一端插入,在另一端刪除,所以只有最早進入隊列的元素才能最先從隊列中刪除,故隊列又稱為先進先出(FIFO—first in first out)線性表。
比如我們去電影院排隊買票,第一個進入排隊序列的都是第一個買到票離開隊列的人,而最后進入排隊序列排隊的都是最后買到票的。
在比如在計算機操作系統中,有各種隊列在安靜的工作著,比如打印機在打印列隊中等待打印。
隊列分為:
①、單向隊列(Queue):只能在一端插入數據,另一端刪除數據。
②、雙向隊列(Deque):每一端都可以進行插入數據和刪除數據操作。
這里我們還會介紹一種隊列——優先級隊列,優先級隊列是比棧和隊列更專用的數據結構,在優先級隊列中,數據項按照關鍵字進行排序,關鍵字最小(或者最大)的數據項往往在隊列的最前面,而數據項在插入的時候都會插入到合適的位置以確保隊列的有序。
?
回到頂部
2、Java模擬單向隊列實現
在實現之前,我們先看下面幾個問題:
①、與棧不同的是,隊列中的數據不總是從數組的0下標開始的,移除一些隊頭front的數據后,隊頭指針會指向一個較高的下標位置,如下圖:
②、我們再設計時,隊列中新增一個數據時,隊尾的指針rear 會向上移動,也就是向下標大的方向。移除數據項時,隊頭指針 front 向上移動。那么這樣設計好像和現實情況相反,比如排隊買電影票,隊頭的買完票就離開了,然后隊伍整體向前移動。在計算機中也可以在隊列中刪除一個數之后,隊列整體向前移動,但是這樣做效率很差。我們選擇的做法是移動隊頭和隊尾的指針。
③、如果向第②步這樣移動指針,相信隊尾指針很快就移動到數據的最末端了,這時候可能移除過數據,那么隊頭會有空著的位置,然后新來了一個數據項,由于隊尾不能再向上移動了,那該怎么辦呢?如下圖:
為了避免隊列不滿卻不能插入新的數據,我們可以讓隊尾指針繞回到數組開始的位置,這也稱為“循環隊列”。
弄懂原理之后,Java實現代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | package?com.ys.datastructure; ? public?class?MyQueue { ????private?Object[] queArray; ????//隊列總大小 ????private?int?maxSize; ????//前端 ????private?int?front; ????//后端 ????private?int?rear; ????//隊列中元素的實際數目 ????private?int?nItems; ????? ????public?MyQueue(int?s){ ????????maxSize = s; ????????queArray =?new?Object[maxSize]; ????????front =?0; ????????rear = -1; ????????nItems =?0; ????} ????? ????//隊列中新增數據 ????public?void?insert(int?value){ ????????if(isFull()){ ????????????System.out.println("隊列已滿!!!"); ????????}else{ ????????????//如果隊列尾部指向頂了,那么循環回來,執行隊列的第一個元素 ????????????if(rear == maxSize -1){ ????????????????rear = -1; ????????????} ????????????//隊尾指針加1,然后在隊尾指針處插入新的數據 ????????????queArray[++rear] = value; ????????????nItems++; ????????} ????} ????? ????//移除數據 ????public?Object remove(){ ????????Object removeValue =?null?; ????????if(!isEmpty()){ ????????????removeValue = queArray[front]; ????????????queArray[front] =?null; ????????????front++; ????????????if(front == maxSize){ ????????????????front =?0; ????????????} ????????????nItems--; ????????????return?removeValue; ????????} ????????return?removeValue; ????} ????? ????//查看對頭數據 ????public?Object peekFront(){ ????????return?queArray[front]; ????} ????? ????? ????//判斷隊列是否滿了 ????public?boolean?isFull(){ ????????return?(nItems == maxSize); ????} ????? ????//判斷隊列是否為空 ????public?boolean?isEmpty(){ ????????return?(nItems ==0); ????} ????? ????//返回隊列的大小 ????public?int?getSize(){ ????????return?nItems; ????} ????? } |
測試:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package?com.ys.test; ? import?com.ys.datastructure.MyQueue; ? public?class?MyQueueTest { ????public?static?void?main(String[] args) { ????????MyQueue queue =?new?MyQueue(3); ????????queue.insert(1); ????????queue.insert(2); ????????queue.insert(3);//queArray數組數據為[1,2,3] ????????? ????????System.out.println(queue.peekFront());?//1 ????????queue.remove();//queArray數組數據為[null,2,3] ????????System.out.println(queue.peekFront());?//2 ????????? ????????queue.insert(4);//queArray數組數據為[4,2,3] ????????queue.insert(5);//隊列已滿,queArray數組數據為[4,2,3] ????} ? } |
回到頂部
3、雙端隊列
雙端隊列就是一個兩端都是結尾或者開頭的隊列,?隊列的每一端都可以進行插入數據項和移除數據項,這些方法可以叫做:
insertRight()、insertLeft()、removeLeft()、removeRight()
如果嚴格禁止調用insertLeft()和removeLeft()(或禁用右端操作),那么雙端隊列的功能就和前面講的棧功能一樣。
如果嚴格禁止調用insertLeft()和removeRight(或相反的另一對方法),那么雙端隊列的功能就和單向隊列一樣了。
?
回到頂部
4、優先級隊列
? 優先級隊列(priority queue)是比棧和隊列更專用的數據結構,在優先級隊列中,數據項按照關鍵字進行排序,關鍵字最小(或者最大)的數據項往往在隊列的最前面,而數據項在插入的時候都會插入到合適的位置以確保隊列的有序。
優先級隊列 是0個或多個元素的集合,每個元素都有一個優先權,對優先級隊列執行的操作有:
(1)查找
(2)插入一個新元素
(3)刪除
一般情況下,查找操作用來搜索優先權最大的元素,刪除操作用來刪除該元素 。對于優先權相同的元素,可按先進先出次序處理或按任意優先權進行。
這里我們用數組實現優先級隊列,這種方法插入比較慢,但是它比較簡單,適用于數據量比較小并且不是特別注重插入速度的情況。
后面我們會講解堆,用堆的數據結構來實現優先級隊列,可以相當快的插入數據。
數組實現優先級隊列,聲明為int類型的數組,關鍵字是數組里面的元素,在插入的時候按照從大到小的順序排列,也就是越小的元素優先級越高。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | package?com.ys.datastructure; ? public?class?PriorityQue { ????private?int?maxSize; ????private?int[] priQueArray; ????private?int?nItems; ????? ????public?PriorityQue(int?s){ ????????maxSize = s; ????????priQueArray =?new?int[maxSize]; ????????nItems =?0; ????} ????? ????//插入數據 ????public?void?insert(int?value){ ????????int?j; ????????if(nItems ==?0){ ????????????priQueArray[nItems++] = value; ????????}else{ ????????????j = nItems -1; ????????????//選擇的排序方法是插入排序,按照從大到小的順序排列,越小的越在隊列的頂端 ????????????while(j >=0?&& value > priQueArray[j]){ ????????????????priQueArray[j+1] = priQueArray[j]; ????????????????j--; ????????????} ????????????priQueArray[j+1] = value; ????????????nItems++; ????????} ????} ????? ????//移除數據,由于是按照大小排序的,所以移除數據我們指針向下移動 ????//被移除的地方由于是int類型的,不能設置為null,這里的做法是設置為 -1 ????public?int?remove(){ ????????int?k = nItems -1; ????????int?value = priQueArray[k]; ????????priQueArray[k] = -1;//-1表示這個位置的數據被移除了 ????????nItems--; ????????return?value; ????} ????? ????//查看優先級最高的元素 ????public?int?peekMin(){ ????????return?priQueArray[nItems-1]; ????} ????? ????//判斷是否為空 ????public?boolean?isEmpty(){ ????????return?(nItems ==?0); ????} ????? ????//判斷是否滿了 ????public?boolean?isFull(){ ????????return?(nItems == maxSize); ????} ? } |
insert() 方法,先檢查隊列中是否有數據項,如果沒有,則直接插入到下標為0的單元里,否則,從數組頂部開始比較,找到比插入值小的位置進行插入,并把 nItems 加1.
remove 方法直接獲取頂部元素。
優先級隊列的插入操作需要 O(N)的時間,而刪除操作則需要O(1) 的時間,后面會講解如何通過 堆 來改進插入時間。
?
回到頂部
5、總結
本篇博客我們介紹了隊列的三種形式,分別是單向隊列、雙向隊列以及優先級隊列。其實大家聽名字也可以聽得出來他們之間的區別,單向隊列遵循先進先出的原則,而且一端只能插入,另一端只能刪除。雙向隊列則兩端都可插入和刪除,如果限制雙向隊列的某一段的方法,則可以達到和單向隊列同樣的功能。最后優先級隊列,則是在插入元素的時候進行了優先級別排序,在實際應用中單項隊列和優先級隊列使用的比較多。后面講解了堆這種數據結構,我們會用堆來實現優先級隊列,改善優先級隊列插入元素的時間。
通過前面講的棧以及本篇講的隊列這兩種數據結構,我們稍微總結一下:
①、棧、隊列(單向隊列)、優先級隊列通常是用來簡化某些程序操作的數據結構,而不是主要作為存儲數據的。
②、在這些數據結構中,只有一個數據項可以被訪問。
③、棧允許在棧頂壓入(插入)數據,在棧頂彈出(移除)數據,但是只能訪問最后一個插入的數據項,也就是棧頂元素。
④、隊列(單向隊列)只能在隊尾插入數據,對頭刪除數據,并且只能訪問對頭的數據。而且隊列還可以實現循環隊列,它基于數組,數組下標可以從數組末端繞回到數組的開始位置。
⑤、優先級隊列是有序的插入數據,并且只能訪問當前元素中優先級別最大(或最小)的元素。
⑥、這些數據結構都能由數組實現,但是可以用別的機制(后面講的鏈表、堆等數據結構)實現。
作者:YSOcean
出處:http://www.cnblogs.com/ysocean/
總結
以上是生活随笔為你收集整理的Java数据结构和算法(五)——队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java数据结构和算法(四)——栈
- 下一篇: Java数据结构和算法(六)——前缀、中