數據結構與算法--隊列

Java數據結構和算法(五)——隊列

 

 


  前面一篇博客咱們講解了並不像數組同樣徹底做爲存儲數據功能,而是做爲構思算法的輔助工具的數據結構——棧,本篇博客咱們介紹另一個這樣的工具——隊列。棧是後進先出,而隊列恰好相反,是先進先出。前端

一、隊列的基本概念

  隊列(queue)是一種特殊的線性表,特殊之處在於它只容許在表的前端(front)進行刪除操做,而在表的後端(rear)進行插入操做,和棧同樣,隊列是一種操做受限制的線性表。進行插入操做的端稱爲隊尾,進行刪除操做的端稱爲隊頭。隊列中沒有元素時,稱爲空隊列。java

  隊列的數據元素又稱爲隊列元素。在隊列中插入一個隊列元素稱爲入隊,從隊列中刪除一個隊列元素稱爲出隊。由於隊列只容許在一端插入,在另外一端刪除,因此只有最先進入隊列的元素才能最早從隊列中刪除,故隊列又稱爲先進先出(FIFO—first in first out)線性表。算法

  好比咱們去電影院排隊買票,第一個進入排隊序列的都是第一個買到票離開隊列的人,而最後進入排隊序列排隊的都是最後買到票的。後端

  在好比在計算機操做系統中,有各類隊列在安靜的工做着,好比打印機在打印列隊中等待打印。數組

  隊列分爲:數據結構

  ①、單向隊列(Queue):只能在一端插入數據,另外一端刪除數據。數據結構和算法

  ②、雙向隊列(Deque):每一端均可以進行插入數據和刪除數據操做。工具

  這裏咱們還會介紹一種隊列——優先級隊列,優先級隊列是比棧和隊列更專用的數據結構,在優先級隊列中,數據項按照關鍵字進行排序,關鍵字最小(或者最大)的數據項每每在隊列的最前面,而數據項在插入的時候都會插入到合適的位置以確保隊列的有序。post

 

二、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]
     }
 
}

  

三、雙端隊列

  雙端隊列就是一個兩端都是結尾或者開頭的隊列, 隊列的每一端均可以進行插入數據項和移除數據項,這些方法能夠叫作:

  insertRight()、insertLeft()、removeLeft()、removeRight()

  若是嚴格禁止調用insertLeft()和removeLeft()(或禁用右端操做),那麼雙端隊列的功能就和前面講的棧功能同樣。

  若是嚴格禁止調用insertLeft()和removeRight(或相反的另外一對方法),那麼雙端隊列的功能就和單向隊列同樣了。

 

四、優先級隊列

   優先級隊列(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) 的時間,後面會講解如何經過 堆 來改進插入時間。

 

五、總結

  本篇博客咱們介紹了隊列的三種形式,分別是單向隊列、雙向隊列以及優先級隊列。其實你們聽名字也能夠聽得出來他們之間的區別,單向隊列遵循先進先出的原則,並且一端只能插入,另外一端只能刪除。雙向隊列則兩端均可插入和刪除,若是限制雙向隊列的某一段的方法,則能夠達到和單向隊列一樣的功能。最後優先級隊列,則是在插入元素的時候進行了優先級別排序,在實際應用中單項隊列和優先級隊列使用的比較多。後面講解了堆這種數據結構,咱們會用堆來實現優先級隊列,改善優先級隊列插入元素的時間。

  經過前面講的棧以及本篇講的隊列這兩種數據結構,咱們稍微總結一下:

  ①、棧、隊列(單向隊列)、優先級隊列一般是用來簡化某些程序操做的數據結構,而不是主要做爲存儲數據的。

  ②、在這些數據結構中,只有一個數據項能夠被訪問。

  ③、棧容許在棧頂壓入(插入)數據,在棧頂彈出(移除)數據,可是隻能訪問最後一個插入的數據項,也就是棧頂元素。

  ④、隊列(單向隊列)只能在隊尾插入數據,對頭刪除數據,而且只能訪問對頭的數據。並且隊列還能夠實現循環隊列,它基於數組,數組下標能夠從數組末端繞回到數組的開始位置。

  ⑤、優先級隊列是有序的插入數據,而且只能訪問當前元素中優先級別最大(或最小)的元素。

  ⑥、這些數據結構都能由數組實現,可是能夠用別的機制(後面講的鏈表、堆等數據結構)實現。

相關文章
相關標籤/搜索