对于队列来说我们可以基于数组和队列写一个,这里我会介绍队列的一些特性,并且通过循环队列来进行对空间复杂度的优化,最后我们可以通过两种队列的不同效率可以明显的看出。
public interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E e);
void dequeue();
E getFront();
}
/**
* 对于队列来说我们一般5个方法,队列是一个先入先出(FIFO)的数据结构,
* int getSize(); 队列的元素个数
* boolean isEmpty(); 队列是否为空
* void enqueue(E e); 入队
* void dequeue(); 出队
* E getFront(); 查看队首元素
*/
public class ArrayQueue<E> implements Queue<E> {
private E [] array;
private int size;
public ArrayQueue(int capacity){
array =(E [])new Object[capacity];
size = 0;
}
public ArrayQueue(){
this(5);
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size==0;
}
@Override
public void enqueue(E e) {
if (size==array.length-1){
resize(array.length*2);
}
array[size] = e;
size++;
}
@Override
public E dequeue() {
if (size<=0){
throw new IllegalArgumentException("队列为空");
}
for (int i=0;i < size;i++){
array[i]=array[i+1];
}
size--;
array[size] = null;
if (size==array.length/4){
resize(array.length/2);
}
return array[0];
}
//查看队首元素
@Override
public E getFront() {
return array[0];
}
private void resize(int newCapacity) {
E [] newData = (E [])new Object[newCapacity];
for (int i= 0;i < size ;i++){
newData[i] = array[i];
}
array=newData;
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append(String.format("Queue: size = %d , capacity = %d\n",size,array.length));
res.append("Queue: ");
res.append("front [");
for (int i =0;i<size ;i++ ){
res.append(array[i]);
if (i !=size-1){
res.append(",");
}
}
res.append("] tail");
return res.toString();
}
}
下面便是循环队列:
/**
* 对于循环数组来说,我们为了让它循环起来,我们通常采用的是取模的方式
* 并且会刻意的多去浪费一个空间,所以我们在初始化的时候会对capacity加
* 上一个1
* 在循环数组中,我们判断队列为空是front,tail是否相等,判断队列满时,我
* 们是将(tail + 1) % data.length == front作为判断条件。
*/
public class LoopQueue<E> implements Queue<E> {
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);
}
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];
}
private void resize(int newCapacity){
E[] newData = (E[])new Object[newCapacity + 1];
for(int i = 0 ; i < size ; i ++)
newData[i] = data[(i + front) % data.length];
data = newData;
front = 0;
tail = size;
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append(String.format("Queue: 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();
}
}
对于两种不同的队列我们可以通过一个小小的测试来看出他们的具体效率如何
/**
* 对于这个测试来说,我们让队列进行100000个数据的出队与入队
* 具体测试效果自己去运行
*/
public static void main(String[] args) {
public static void main(String[] args) {
ArrayQueue<Integer> arrayQueue=new ArrayQueue<>();
int opCount = 100000;
Long startTime = System.nanoTime();
Random random=new Random();
for (int i = 0;i< opCount ;i++){
arrayQueue.enqueue(random.nextInt(Integer.MAX_VALUE));
}
for (int i = 0;i<opCount;i++){
arrayQueue.dequeue();
}
Long endTime = System.nanoTime();
System.out.println("普通的数组队列:"+(endTime - startTime)/1000000000.0) ;
System.out.println("循环队列的操作");
LoopQueue<Integer> loopQueue=new LoopQueue<>();
Long startTime1 = System.nanoTime();
for (int i = 0;i< opCount ;i++){
loopQueue.enqueue(random.nextInt(Integer.MAX_VALUE));
}
for (int i = 0;i<opCount ;i++){
loopQueue.enqueue(random.nextInt(Integer.MAX_VALUE));
}
for (int i=0;i<opCount;i++){
loopQueue.dequeue();
}
Long endTime1 = System.nanoTime();
System.out.println("循环队列:"+(endTime1 - startTime1)/1000000000.0) ;
}