隊列的特色是節點的排隊次序和出隊次序按入隊時間前後肯定,即先入隊者先出隊,後入隊者後出隊。即咱們常說的FIFO(first in first out)先進先出。 java
順序存儲結構存儲的隊列稱爲順序隊列,內部使用一個一維數組存儲,用一個隊頭指針front指向隊列頭部節點(即便用int類型front來表示隊頭元素的下標),用一個隊尾指針rear,指向隊列尾部元素(int類型rear來表示隊尾節點的下標)。數組
初始化隊列時: front = rear = -1 (非必須,也可設置初始值爲0,在實現方法時具體修改)this
隊列滿時: rear = maxSize-1 (其中maxSize爲初始化隊列時,設置的隊列最大元素個數)spa
隊列爲空時: front = rear指針
下面使用java實現一個基於一維數組的順序隊列,代碼以下:code
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方法):blog
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)io
執行queue.add(1)方法後隊列的狀態以下圖:class
能夠看到向隊列中添加元素後,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,以達到循環使用隊列空間的目的。