循环队列的几个特性:
循环队列是对数组队列的进一步优化,
具体表现为 , 当移除某一元素时, 队列中的首元素索引 +1, 而不是将元素整体先前挪动一位
当尾元素至容器尾端时 , 会将新元素保存至队列首段的曾被移除过的索引处保存.
首元素索引 : front ; 尾元素索引+1 : tail ,也就是tail指向最后一个队尾的下一个位置;
在队列设计中 , 通过牺牲一个空间用于判断队列的空间剩余. 所以在队列初始化时, 程序内部需要多初始化一个容量
如何判断容器是否满 , (tail+1) % 队列容量 == front 来判断
如何判断队列为空,即front与tail指向同一位置时
为了使用方便起见,同样使用了泛型,以及默认存储容量为10,如果在实例化的时候传入容量也是可以的,这里同样赋予了构造方法
private E[] data;
/**
* front 指向队列头部元素
* tail 指向尾部元素的下一个
*/
private int front, tail;
private int size;
public LoopQueue(int capaticy) {
//因为要空出一个位置来进行判断,所以要多开辟一位空间
data = (E[]) new Object[capaticy + 1];
front = 0;
tail = 0;
size = 0;
}
/**
* 初始化数组队列长队为 10
*/
public LoopQueue() {
this(10);
}
首先是一个入队操作,在入队之前也有可能做了很多其他操作,尾指针不能一直加加,如果到数组尾部的时候,就得将新的这个元素放置在数组的头部了
/**
* 向队列中添加元素
*
* @param e
*/
@Override
public void enqueue(E e) {
//判断队列是否满员 如果数组满员 以当前数组容量的二倍开辟新的存储空间
if ((tail + 1) % data.length == front) {
resize(getCapacity() * 2);
}
data[tail] = e;
tail = (tail + 1) % data.length;
size++;
}
出队操作同样也有这样的逻辑,把循环队列比喻成一个环
/**
* 删除队首元素
* @return
*/
@Override
public E dequeue() {
if (isEmpty()) {
throw new IllegalArgumentException("Can't dequeue from an empty queue");
}
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size--;
//当队列中存储的元素不及容量的 1/4 时 , 将数组缩容原长的 1/2
if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
resize(getCapacity() / 2);
}
return ret;
}
在重定义大小的时候同样也有这样的逻辑,这是将原先的数组从front—(front+size)%data.length的数据放置进新的数组的 0 —size-1的位置
/**
* 重新定义数组的长度
*
* @param newCapacity
*/
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;
}
整段代码如下:
/**
* 循环队列
* 采用空出一个元素的方法判断队列是否为空或者满
*
* @param <E>
*/
public class LoopQueue<E> implements Queue<E> {
private E[] data;
/**
* front 指向队列头部元素
* tail 指向尾部元素的下一个
*/
private int front, tail;
private int size;
public LoopQueue(int capaticy) {
//因为要空出一个位置来进行判断,所以要多开辟一位空间
data = (E[]) new Object[capaticy + 1];
front = 0;
tail = 0;
size = 0;
}
/**
* 初始化数组队列长队为 10
*/
public LoopQueue() {
this(10);
}
/**
* 获得当前数组队列的容量
*
* @return
*/
public int getCapacity() {
return data.length - 1;
}
/**
* 循环队列为空的标志即为头和尾指向同一个位置
*
* @return
*/
@Override
public boolean isEmpty() {
return front == tail;
}
/**
* 获取当前队列中元素个数
*
* @return
*/
@Override
public int getSize() {
return size;
}
/**
* 向队列中添加元素
*
* @param e
*/
@Override
public void enqueue(E e) {
//判断队列是否满员 如果数组满员 以当前数组容量的二倍开辟新的存储空间
if ((tail + 1) % data.length == front) {
resize(getCapacity() * 2);
}
data[tail] = e;
tail = (tail + 1) % data.length;
size++;
}
/**
* 删除队首元素
* @return
*/
@Override
public E dequeue() {
if (isEmpty()) {
throw new IllegalArgumentException("Can't dequeue from an empty queue");
}
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size--;
//当队列中存储的元素不及容量的 1/4 时 , 将数组缩容原长的 1/2
if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
resize(getCapacity() / 2);
}
return ret;
}
/**
* 获取队首元素
* @return
*/
@Override
public E getFront() {
if (isEmpty()) {
throw new IllegalArgumentException("Queue is empty.");
}
return data[front];
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(String.format("Queue : size %d , capacity % d\n", size, getCapacity()));
result.append("front : [");
for (int i = front; i != tail; i = (i + 1) % data.length) {
result.append(data[i]);
if ((i + 1) % data.length != tail) {
result.append(" ,");
}
}
result.append("] tail");
return result.toString();
}
/**
* 重新定义数组的长度
*
* @param newCapacity
*/
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;
}
}
在上一章已经测试过性能了
在这里测试下运行结果:在循环队列中添加0—9,元素,在添加完2,5,8的时候做一次出队操作
public class 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) { queue.dequeue(); System.out.println(queue); } } } }