什么是队列:队列是只允许在一端进行插入操作,而在另一端进行删除操作. 队列是一种先进先出(FIFO)的线性表,允许插入的一端称为队尾,允许删除的一端称为队头.
首先创建Queue接口,面向接口编程,其中接口中创建以下方法:
getSize:获取队列中的元素个数
isEmpty:查看队列中是否为空
enqueue:入队 向队尾添加元素
dequeue:出队 将队首的元素拿出
getFront:查看队首的元素
public interface Queue
{
int getSize();
boolean isEmpty();
void enqueue(E e);
E dequeue ();
E getFront();
}
数组队列,顾名思义,此队列底层是用数组实现的,这里就不详细讲解数组代码的实现过程。
请参考https://blog.csdn.net/qq_44313091/article/details/97539644
创建ArrayQueue类,来完成数组队列的实现。
public class ArrayQueue
implements Queue {
private Arrayarray;
public ArrayQueue (int capacity) {
array =new Array<>(capacity);
}
public ArrayQueue () {
array =new Array<>();
}
以下代码重写了Queue接口中的方法,还重写了toString的方法。
@Override
public int getSize () {
return array.getsize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
public int getCapacity() {
return array.getcapacity();
}
@Override
public void enqueue (E e) {
array.addLast(e);
}
@Override
public E dequeue () {
return array.removeFirst();
}
@Override
public E getFront() {
return array.get(0);
}@Override
public String toString () {
StringBuilder res =new StringBuilder();
res.append("Queue:");
res.append("front [");
for(int i=0;ires.append(array.get(i));
if(i!=array.getsize()-1) {
res.append(",");
}
}
res.append("] tail");
return res.toString();
}
void enqueue (E) o(1)(均摊)
E dequeue() o(n)
E front() o(1)
因为取出队首的元素时,队列中的所有元素都要向前移动一位,所以时间复杂度为o(n);正因为出队的时间复杂度为o(n),增大了运行时间,所以提出了循环队列的概念,将出队的时间复杂度变为o(1)
下面讲解一下循环队列的实现思路,循环队列最底层还是以数组来实现。
图1,front指向对首元素,tail指向待添加的元素地址。当front和tail指向相同时表示队列为空。
图2中,拿出队首的元素时,不需要将所有的元素前移一位,只需要维护front的指向即可。向队列中添加元素只需要维护tail的指向就可以。
图3中,当不断地添加元素,导致tail不能继续++,这时大家可以发现,当取出队首的元素时,前面的空间没有被利用,导致前面的空间剩余,这时可以将tail指向前面的空余的空间,这就可以将数组看成一个环状,充分利用了数组的所有空间。
图4,当又向队列中添加一个元素时,如图所示,这时将不能继续添加元素,因为继续添加元素,tail即会和front相等,图1中说道,当tail与front指向相同时,表示队列为空。 (tail+1)%c==front表示队列已满,这里的c表示数组的容量。
创建LoopQueue类,实现接口Queue。底层创建数组,因为循环队列会浪费数组中的一个空间,所以构造方法中是capacity+1。
public class LoopQueue
implements Queue {
private E [] data;
private int front,tail;
private int size;
public LoopQueue (int capacity) {
data =(E[])new Object[capacity+1];
front =0;
tail =0;
size =0;
}
public LoopQueue () {
this(10);
}
getCapacity:返回队列的容量
isEmpty:查看队列是否为空,当front==tail时,队列为空
getSize:返回队列中元素个数
enqueue:向队尾添加元素。首先判断队列是否已满,若满则将队列扩容为原来的2倍;否则,将e添加到tail指向的空间,维护tail和size。
dequeue:取出队首元素。先判断队列是否为空,若为空则抛出异常;否则,返回并删除ront指向的元素,维护front、size。若队列的元素个数为容量的1/4,将队列容量缩小为原队列的1/2。这里是防止频繁的掉头resize方法。
getfront:先判断队列是否为空,若为空则抛出异常;否则,返回front指向的元素。
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) {
if((tail+1)%data.length==front) {
resize (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==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];
}
resize:对队列容量进行修改,以充分利用内存空间。将原队列front指向的元素放到新队列的首项。
private void resize(int newCapacity) {
E[] newData =(E[])new Object [newCapacity+1];
for(int i =0;inewData[i] = data[(i+front)%data.length];
}
data =newData ;
front =0;
tail =size;
}
最后重写toString方法
@Override
public String toString() {
StringBuilder res =new StringBuilder();
res.append(String.format("Array: 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+1)%data.length!=tail) {
res.append(",");
}
}
res.append("] tail ");
return res.toString();
}
testQueue:传入Queue类型,opCount值。方法开始进行计时,一个for循环将opCount个随机数存到队列中,第二个for循环将存入的数逐一取出,最后返回运行时间。
private static double testQueue (Queue
q , int opCount) {
long startTime =System.nanoTime();
Random rd =new Random();
for(int i=0;iq.enqueue(rd.nextInt(Integer.MAX_VALUE));
}
for(int i=0;iq.dequeue();
}
long endTime =System.nanoTime();
return (endTime-startTime)/1000000000.0;
}
public static void main(String[] args) {
int opCount =100000;
ArrayQueuearrayqueue =new ArrayQueue <>();
double time1 =testQueue(arrayqueue,opCount);
System.out.println("ArrayQueue,time:"+time1+"s");
LoopQueueloopqueue =new LoopQueue <>();
double time2 =testQueue(arrayqueue,opCount);
System.out.println("LoopQueue,time:"+time2+"s");
}
}