2.5循環隊列

在正式進行循環隊列學習以前,咱們先來看看在順序隊列中刪除隊首元素出現的問題html

(1)設一個容量爲capacity=8,size=5(a,b,c,d,e)的數組,左側爲隊首、右側爲隊尾。java

(2)出隊一個元素後,需總體往前移動一位git

#出隊github

  

#2總體前移一位數組

關於該種操做方式咱們很容易得出時間複雜度爲O(n)。app

 

這時咱們就想可不能夠在出隊元素後,總體元素不往前移,而是在數組中記下隊首front是誰,同時隊尾tail指向在下一次元素入隊時的位置,這樣當再有出隊時只須要維護一下front的指向便可,而不需移動元素。就這樣咱們就有了循環隊列的狀況。ide

2.循環隊列原理

(1)初始,數組總體爲空時,隊首front、隊尾tail指向同一個位置(數組索引爲0的地方)也即front==tail 時隊列爲空函數

 

(2)當往數組中添加元素後,oop

(3)出隊一個元素,front指向新的位置學習

(4)入隊元素,tail疊加

(5)當tail不能再增長時,數組前面還有空餘,此時循環隊列就該出場了。

 

此時數組應該變爲這樣:

 在往數組中添加一個元素:

這樣數組就已經滿了(tail+1==front 隊列滿),開始出發擴容操做。【capacity中,浪費一個空間】。

爲了tail能返回到數組的前面位置,將隊列滿的表達式變爲 【(tail+1)%c==front】這樣數組就能夠循環移動了。

3.循環隊列代碼實現

新建一個類LoopQueue並實現接口Queue。

#1:接口Queue代碼以下:

package Queue; public interface Queue<E> { //獲取隊列中元素個數
    int getSize(); //隊列中元素是否爲空
    boolean isEmpty(); //入隊列
    void enqueue(E e); //出隊列
 E dequeue(); //獲取隊首元素
 E getFront(); }

#2:LoopQueue相關代碼:

 1 package Queue;  2 
 3 //循環隊列
 4 public class LoopQueue<E> implements Queue<E> {  5     private E[] data;  6     private int front, tail;  7     private int size;//隊列中元素個數  8 
 9     //構造函數,傳入隊列的容量capacity構造函數
 10     public LoopQueue(int capacity) {  11         data = (E[]) new Object[capacity + 1];//浪費與一個空間
 12         front = 0;  13         tail = 0;  14         size = 0;  15  }  16 
 17     //無參構造函數,默認隊列的容量capacity=10
 18     public LoopQueue() {  19         this(10);  20  }  21 
 22     //真正容量
 23     public int getCapacity() {  24         return data.length - 1;  25  }  26 
 27     //隊列是否爲空
 28  @Override  29     public boolean isEmpty() {  30         return front == tail;  31  }  32 
 33     //隊列中元素個數
 34  @Override  35     public int getSize() {  36         return size;  37  }  38 
 39     //入隊列操做
 40  @Override  41     public void enqueue(E e) {  42         if ((tail + 1) % data.length == front) {//隊列已滿,須要擴容
 43             resize(getCapacity() * 2);  44  }  45         data[tail] = e;  46         tail = (tail + 1) % data.length;  47         size++;  48  }  49 
 50     //出隊操做
 51 
 53  @Override  54     public E dequeue() {  55         if (isEmpty()) {  56             throw new IllegalArgumentException("隊列爲空");  57  }  58 
 59         E ret = data[front];  60         data[front] = null;//手動釋放  61         front = (front + 1) % data.length;  62         size--;  63         if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {  64             resize(getCapacity() / 2);  65  }  66         return ret;  67  }  68 
 69     //獲取隊首元素
 70  @Override  71     public E getFront() {  72         if (isEmpty()) {  73             throw new IllegalArgumentException("隊列爲空");  74  }  75         return data[front];  76  }  77 
 78     //改變容量
 79     private void resize(int newCapacity) {  80         E[] newData = (E[]) new Object[newCapacity + 1];  81         for (int i = 0; i < size; i++) {  82             newData[i] = data[(front + i) % data.length];//循環數組防止越界
 83  }  84         data = newData;  85         front = 0;  86         tail = size;  87  }  88 
 89 
 90  @Override  91     public String toString() {  92         StringBuilder res = new StringBuilder();  93         res.append(String.format("Queue:size=%d, capacity=%d\n", size, getCapacity()));  94         res.append("front [");  95         for (int i = front; i != tail; i = (i + 1) % data.length) {  96  res.append(data[i]);  97             if ((i + 1) % data.length != tail) {  98                 res.append(",");  99  } 100  } 101         res.append("] tail"); 102         return res.toString(); 103  } 104 
105 
10121 
122 }

在關於LoopQueue類中須要注意的:

(1)第11行中的+1是capacity須要浪費一個空間,故在實例化是多加1

data = (E[]) new Object[capacity + 1];//浪費與一個空間

(2)地24行真正的容量是data.length-1,這是因爲有一個空間是浪費的。

data.length - 1;

(3)關於入隊中第46行tail值的說明

爲了保證入隊是循環操做,tail值的變化規律爲

tail = (tail + 1) % data.length;

(4)關於82行的數據遷移操做,取餘操做是爲了防止循環數組時越界。

newData[i] = data[(front + i) % data.length];//循環數組防止越界

#3直接在LoopQueue中添加一個main函數進行測試,相關代碼以下:

//測試用例
    public static void main(String[] args) { LoopQueue<Integer> queue = new LoopQueue<Integer>(); for (int i = 0; i < 10; i++) { queue.enqueue(i); System.out.println(queue); if(i%3==2){//每添加3個元素出隊列一個
 queue.dequeue(); System.out.println(queue); } } }

 

結果爲:

 

 

4.循環隊列時間複雜度

到此咱們就實現了一個循環隊列操做,解決了在順序隊列中出隊時的時間複雜度爲O(n)的狀況,在循環隊列中出隊的時間複雜度爲O(1)。

 

源碼地址 https://github.com/FelixBin/dataStructure/blob/master/src/Queue/LoopQueue.java

原文出處:https://www.cnblogs.com/wfaceboss/p/10628345.html

相關文章
相關標籤/搜索