java實現隊列


隊列的定義

  隊列的特點是節點的排隊次序和出隊次序按入隊時間先后確定,即先入隊者先出隊,后入隊者后出隊。即我們常說的FIFO(first in first out)先進先出。    

順序隊列定義及相關操作

  順序存儲結構存儲的隊列稱為順序隊列,內部使用一個一維數組存儲,用一個隊頭指針front指向隊列頭部節點(即使用int類型front來表示隊頭元素的下標),用一個隊尾指針rear,指向隊列尾部元素(int類型rear來表示隊尾節點的下標)。

  初始化隊列時: front = rear = -1 (非必須,也可設置初始值為0,在實現方法時具體修改)

  隊列滿時:     rear = maxSize-1 (其中maxSize為初始化隊列時,設置的隊列最大元素個數)

  隊列為空時:  front = rear

  下面使用java實現一個基於一維數組的順序隊列,代碼如下:

 1 /**
 2  * 定義一個queue
 3  */
 4 class ArrayQueue{
 5     private int[] data ; //隊列中存放的數據
 6     private int maxSize ; //隊列的大小
 7     private int front ;//指向隊列頭部的指針
 8     private int rear ; //指向隊列尾部的指針
 9 
10     public ArrayQueue(int maxSize){
11         this.maxSize = maxSize;
12         data = new int[maxSize];
13         front = -1;
14         rear = -1;
15     }
16 
17     /**
18      * 判斷隊列是否已滿
19      * @return
20      */
21     public boolean isFull(){
22         return rear == maxSize -1 ;
23     }
24 
25     /**
26      * 判斷隊列是否為空
27      * @return
28      */
29     public boolean isEmpty(){
30         return rear == front;
31     }
32 
33     /**
34      * 添加數據到隊列
35      * @param n
36      */
37     public void add(int n){
38         if(isFull()){
39             System.out.println("隊列已滿,不能添加");
40             return;
41         }
42         data[++rear] = n;
43     }
44 
45     /**
46      * 顯示頭部數據
47      * @return
48      */
49     public void head(){
50         if(isEmpty()){          
      throw new RuntimeException("隊列為空"); 53 } 54 System.out.println(data[front+1]); 55 } 56 57 /** 58 * 取出頭部數據 59 * @return 60 */ 61 public int pop(){ 62 if(isEmpty()){
64 throw new RuntimeException("隊列為空"); 65 } 66 int a = data[++front]; 67 data[front] = 0; 68 return a; 69 } 70 71 /** 72 * 打印全部數據 73 */ 74 public void print(){ 75 if(isEmpty()){ 76 System.out.println("隊列為空"); 77 return; 78 } 79 for(int i=0;i<data.length;i++){ 80 System.out.printf("array["+i+"]=%d\n",data[i]); 81 } 82 } 83 }

  簡單描述順序隊列的入隊(add方法):

  

 1 public static void main(String []args) {
 2         //1.聲明一個可以存儲6個元素的順序隊列,默認值為0,front 和rear指針為-1
 3         ArrayQueue queue = new ArrayQueue(6);
 4         //2.向順序隊列中添加元素
 5         queue.add(1);
 6         queue.add(2);
 7         queue.add(3);
 8         queue.add(4);
 9         queue.add(5);
10         queue.add(6);
11         //2.1打印當前隊列元素
12         queue.print();
13         //3.將順序隊列中元素取出
14         queue.pop();
15         queue.pop();
16         queue.pop();
17         queue.pop();
18         queue.pop();
19         queue.pop();
20         //4.隊列中元素全部取出
21 }

  在代碼中初始化了一個大小為6的順序隊列,下圖展示了第一步(即代碼ArrayQueue queue = new ArrayQueue(6))中隊列元素及指針情況

 

  其中front和rear指向的虛線框實際並不存在,僅用來表示初始化時的默認狀態,因我們實現的隊列元素使用int[]存儲元素,所以初始值均為0(如用Object[]或范型則初始值為null)

      執行queue.add(1)方法后隊列的狀態如下圖:

 

  可以看到向隊列中添加元素后,rear指針向后移動一個位置指向第一個元素位置,后面繼續添加后面5個元素后隊列如下圖所示

  接下來看下隊列的出隊情況 

  當第一次執行queue.pop()方法后,隊列元素如上圖所示,此時隊列剩下5個元素

  當第六次執行queue.pop()方法后,隊列元素如下圖所示

  此時隊列中元素已全部出隊,按正常邏輯應該可以添加元素到隊列中,但此時添加元素卻會報隊列已滿錯誤(rear=maxSize-1),當然即使前面元素未出隊也會報相同錯誤。這就是我們常說的“假溢出”問題。為解決這個問題,就引出了我們的環形隊列。

環形隊列

  環形隊列,顧名思義即讓普通隊列首尾相連,形成一個環形。當rear指向尾元素后,當隊列有元素出隊時,可以繼續向隊列中添加元素。

  這里我使用的是rear指針指向最后一個節點的后一個元素,即會占用一個位置用來表示隊列已滿。

  

  初始化隊列時: front = rear = 0

  隊列滿時:    ( rear +1 ) % maxSize == front (其中maxSize為初始化隊列時,設置的隊列最大元素個數)

  這里不能使用rear = maxSize-1作為判斷隊滿的條件,因使用環形隊列方式實現,當第一次隊滿時,rear = maxSize -1,執行出隊操作后原隊頭位置空出,此時繼續執行入隊操作,則rear向后移動一個位置,則rear = 0,而此時隊列也是已滿狀態。所以只要rear 向前移動一個位置就等於front時,就是隊滿的情況。

  隊列為空時:  front == rear

  先看下具體代碼

  

 1 public class CycleQueue {
 2 
 3     /**
 4      *
 5      */
 6     private int maxSize ;
 7     private int data[] ;
 8     private int front ;
 9     /**
10      * 這里rear指向最后一個數據的后面一個位置,即隊列中有一個為空占位
11      */
12     private int rear ;
13 
14     public CycleQueue(int maxSize){
15         this.maxSize = maxSize;
16         data = new int[maxSize];
17         front = 0;
18         rear = 0;
19     }
20 
21     /**
22      * 判斷隊列是否已滿
23      * 因是循環隊列,所以rear值可能小於front,所以不能使用rear == maxSize -1來判斷
24      * @return
25      */
26     public boolean isFull(){
27         return (rear + 1) % maxSize == front;
28     }
29 
30     public boolean isEmpty(){
31         return rear == front;
32     }
33 
34     public void add(int n){
35         if(isFull()){
36             System.out.println("隊列已滿,不能添加");
37             return;
38         }
39         data[rear] = n;
40         rear = (rear + 1) % maxSize;
41     }
42 
43     public void head(){
44         if(isEmpty()){
45             throw new RuntimeException("隊列為空");
46         }
47         System.out.println("head="+data[front]);
48     }
49 
50     public int pop(){
51         if(isEmpty()){
52             throw new RuntimeException("隊列為空");
53         }
54         int value = data[front];
55         front = (front + 1) % maxSize;
56         return value;
57     }
58 
59     public void print(){
60         if(isEmpty()){
61             System.out.println("隊列為空");
62             return;
63         }
64         for(int i= front; i<front+size(); i++){
65             System.out.printf("array"+(i%maxSize)+"=%d",data[i%maxSize]);
66         }
67     }
68 
69     /**
70      * 因是循環隊列,所以會出現rear<front情況,這里需要+maxSize
71      * @return
72      */
73     public int size(){
74         return (rear - front + maxSize)%maxSize;
75     }
76 }

  下面再以圖解的方式講解一下環形隊列的入隊出隊以及隊滿情況。當執行初始化代碼后

1 CycleQueue queue = new CycleQueue(6);   

  此時front = rear = 0,隊列為空。當第一次執行queue.add(1)后,環形隊列元素如下圖所示

 

  當依次執行queue.add(2);queue.add(3);queue.add(4);queue.add(5);后,達到(rear+1)%maxSize=front (即rear=5)條件,隊列已滿不能添加新元素。此時環形隊列元素情況如下圖

 

  所以這種情況會浪費一個空間來作為判滿的條件。

  下面執行出隊操作,當第一次執行出隊操作queue.pop()方法后,環形隊列元素情況如下圖所示

   此時 (rear+1)%maxSize = 0  不等於 front=1 ,所以可以繼續向隊列中添加元素,也就不會出現假溢出的情況。當執行入隊(例queue.add(6))操作后,rear = (rear+1)%maxSize 即rear=0,以此來生成環形隊列。此時隊列元素情況如下圖所示

  另外,再說明下環形隊列有效元素個數問題,如果不是環形隊列,則有效元素個數size = rear - front。而使用環形實現后,會出現rear<front的情況,所以這里使用(rear-front+maxSize)%maxSize的方式計算有效元素個數。(或者在內部定義一個size屬性,當元素入隊時size++,當出隊時size--)

  因此在打印隊列中元素時,從front位置開始至 front+size位置結束來循環打印有效元素。

總結

  如果不實用環形隊列方式實現隊列,則會出現“假溢出”情況(即隊列滿后,將全部元素出隊卻不能繼續添加元素的情況)。而環形隊列會在隊頭元素出隊后,將隊尾指針rear重新分配為0,以達到循環使用隊列空間的目的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM