思路分析:
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
文章视频链接:https://www.ixigua.com/i6815911054443282948/
原文地址:https://www.cnblogs.com/study-gdp/p/12705223.html
时间: 2024-10-13 22:59:05