思路分析:
1.記錄數組的隊首和隊尾的位置,當front 和tail指在一起的時候數組為空。
2.出隊的時候front指針往后挪一位。這樣出隊操作就由數組隊列的 O(N) 變成 循環隊列的O(1)了。
讓數組循環利用起來:
當前索引+1 再百分之我們數組的長度 比如我們到了最后一位7, 7+1 = 8 就是我們數組的長度 8對8 求余 = 0
就跟鍾表一樣 找到它的范圍 然后讓它在范圍內循環。 1%12 = 1; 2 % 12 = 2; .... 11%12=11 12%12 = 0 這樣就能無限在范圍內循環。
然而有一個問題我們front = tail 表示空數組,也可以表示滿數組。為了達到區分我們要刻意浪費一個空間,tail+1 = front = 滿數組!!
package com.dapeng.Queue; /** * @author gdp * @date 2020/4/5 23:06 */ public class LoopQueue<E> implements Queue<E> { private E[] data; private int front, tail; //數據首位 front==tail的時候空數組 但是也代表數組滿了 所以我們設計為tail+1 為數組滿了有意的浪費一個空間來做出區分 private int size; //數組數據個數 public LoopQueue(int capacity){ data = (E[]) new Object[capacity+1]; //滿足了用戶需求也滿足了程序設計 front = 0 ; tail = 0 ; size = 0 ; } public LoopQueue(){ this(10); } public int getCapacity(){ return data.length -1; } @Override public boolean isEmpty(){ return front == tail;} @Override public int getSize(){ return size;} @Override public void enqueue(E e){ //最后一個元素+1 取模 if( (tail + 1) % data.length == front ) resize(this.getCapacity() * 2 ); data[tail] = e; tail = (tail +1 ) % data.length ; size ++; } @Override public E dequeue(){ if(isEmpty()) throw new IllegalArgumentException("Cannot dequeue from an empty queue."); E ret = data[front]; data[front] = null; front = (front +1) % data.length; size --; if(size == this.getCapacity() / 4 && getCapacity()/2 != 0) { resize(getCapacity() / 2); } return ret; } @Override public E getFront(){ if(isEmpty()) throw new IllegalArgumentException(" Queue is empty !."); return data[front]; } private void resize(int newCapacity) { E[] newData = (E[]) new Object [newCapacity +1]; for(int i = 0; i< size; i++ ) { newData[i] = data[(front + i) % data.length]; data = newData; front = 0; tail = size; } } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append(String.format("Queen: size = %d, capacity = %d\n",size,getCapacity())); res.append("front ["); for(int i = front; i != tail; i = (i+1) % data.length) { res.append(data[i]); if(i!=size-1) res.append(","); } res.append("]tail"); return res.toString(); } public static void main(String[] args) { LoopQueue<Integer> queue = new LoopQueue<>(); for(int i = 0 ; i < 10 ; i++ ){ queue.enqueue(i); /* if(i % 3 == 2){ queue.dequeue(); System.out.println(queue); }*/ } System.out.println(queue.toString()); } }
這個是測試兩種隊列效率的代碼:顯然循環隊列是快了很多的。
package com.dapeng.Queue; import java.util.Random; /** * @author gdp * @date 2020/4/6 18:41 */ public class queueTest { private static double test(Queue<Integer> queue, int opCount){ long startTime = System.nanoTime(); Random random = new Random(); for (int i = 0; i< opCount ; i++) queue.enqueue(random.nextInt(Integer.MAX_VALUE)); for (int i = 0; i< opCount ; i++) queue.dequeue(); long endTime = System.nanoTime(); return (endTime - startTime) / 100000000.0; } public static void main(String[] args) { int opCount = 1000000; ArrayQueue<Integer> arrayQueue = new ArrayQueue<>(); double time1 = test(arrayQueue, opCount); System.out.println("ArrayQueue, time:" +time1+" s"); LoopQueue<Integer> LoopQueue = new LoopQueue<>(); double time2 = test(LoopQueue, opCount); System.out.println("LoopQueue, time:" +time2+" s"); } }
兩個耗時結果比較:
ArrayQueue, time:2926.524975 s
LoopQueue, time:0.457739 s