循环队列的思想
LoopQueue循环队列的内部也是一个动态数组;
在队列中,front指向队首,tail指向队尾的下一个index,也就是下个元素入队的位置。
size代表循环队列中有效元素的个数。
循环队列的规定事项
队列为空:
front == tail 时队列为空
队列为满:
因为上面的规定,而且tail总是指向最后一个元素的下一个位置,所以为了避免与上面冲突,在这里规定LoopQueue中要浪费一个位置
(tail +1) % c = front 时,队列满
几个变量的解释
1、getCapacity() : 数组容量,在创建循环队列时,用户传入的队列大小(实际上是数组的大小,并且这个数组在使用时会动态扩容)
2、data.length : data是LoopQueue中定义的类变量,是一个数组。它的长度代表了LoopQueue的实际大小,这个对用户是隐藏的
3、size:有效元素的个数
4、tail入队enqueue时增加移动,入队时front不动
front出队时dequeueu增加移动,出队时tail不动
data.length = getCapacity() + 1
队列接口
接口Queue规定了必须实现的方法,这些
public interface Queue {
void enQueue(E e);
E deQueue();
E getFront();
boolean isEmpty();
int getSize();
}
循环队列实现的方法
一、变量
private int front; //指向队列头部
private int tail; //指向队列最后一个元素的下一个索引,也就是即将下一个被插入的位置
private int size; //队列有效元素的个数
private E[] data; //泛型数组 所以在本篇中LoopQueue的底层其实是线性动态数组
二、构造函数
构造函数
//有参构造函数,并且初始化变量
public LoopQueue(int capacity){
== data = (E[])new Object[capacity + 1]; == //注意这里,实际new出的数组比设想new出的大1
front = 0;
tail = 0;
size = 0;
}
//无参构造函数,调用this(),也就是调用了上面有参的构造函数
public LoopQueue() { this(10); }
三、基本方法
int getCapacity() data.length -1
int getSize()
boolean isEmpty() front == tail
String toString()
四、功能方法
void enQueue(E e)
E deQueue()
E getFront()
void resize(int newCapacity)
enQueue前先判断是否满了,满了就resize
再入队
然后依次改变front tail size
deQueue前先判断是否为空
不空时:出队,被出队的位置要null,防止闲逛对象
然后front移动,改变size
resize()
1 newCapacity 2 data依次放入newData 3 data指向改变 4 改变front tail
循环队列实现中的一些需要注意的点
位移变量用data.length
扩容用getCapacity()
有效数组个数用size
循环队列的循环方法
方法一:
i从0到size循环-------然后front依次加上循环i
for(i=0; i<size; i++)
data[(front + i) % data.length] 可以表示每个元素从front到tail
方法二:
i从front循环到tail(这里写成!=tail)----然后直接使用i
for(i=front; i!=tail; i = (i+1)%data.length )
data[i] 可以表示每个元素从front到tail
*循环队列计算front和tail时要循环起来,利用%
移动front和tail时也要考虑循环%
循环队列的复杂度分析
Queue
void enqueue(E) 均摊O(1)
E dequeue() 均摊O(1)
E getFront() O(1)
int getSize() O(1)
boolean isEmpty() O(1)
LoopQueue的实现
public class LoopQueue<E> implements Queue<E>{
private int front; //指向队列头部
private int tail; //指向队列最后一个元素的下一个索引,也就是即将下一个被插入的位置
private int size; //队列有效元素的个数
private E[] data; //泛型数组
//有参构造函数,并且初始化变量
public LoopQueue(int capacity){
data = (E[])new Object[capacity + 1]; //注意这里,实际new出的数组比设想new出的大1
front = 0;
tail = 0;
size = 0;
}
//无参构造函数,调用this(),也就是调用了上面有参的构造函数
public LoopQueue() {
this(10);
}
//获取循环队列中的容量(比实际队列大小少1)
public int getCapacity() {
return data.length -1;
}
//获取循环队列中的有效元素个数
@Override
public int getSize() {
return size;
}
//判断循环队列是否为空
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public void enQueue(E e) {
//首先判断队列是否为满
//有位移循环时要用 data.length,而不是getCapacity()
if((tail + 1) % data.length == front) {
resize(getCapacity() * 2); //之前循环队列用户定义的大小再*2
}
//然后入队尾,更改tail和size
data[tail] = e;
tail = (tail + 1) % data.length; //tail移动需要用 %
size ++;
}
@Override
public E deQueue() {
//首先判断队列是否为空(空不能出队)
if (front == tail) {
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
}
E frontElement;
frontElement = data[front];
data[front] = null; //注意这里,data[front]出循环队列了,那么原本存在这里的需要释放,否则会造成loitering objects 闲逛对象
front = (front + 1) % data.length; //front移动需要用 %
size --;
if (size == getCapacity() / 4 && getCapacity() / 2 != 0) { //注意下面调用resize(参数),这个参数!=0 ,要判断一下
resize(getCapacity() / 2);
}
return frontElement;
}
@Override
public E getFront() {
//首先判断队列是否为空(空不能出队)
if (front == tail) {
throw new IllegalArgumentException("LoopQueue is empty.");
}
return data[front];
}
private void resize(int newCapacity){
E[] newData = (E[])new Object[newCapacity + 1]; //这里要注意,再次扩容/缩容时,都要有这个+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("LoopQueue : front [");
for (int i = front; i != tail; i = (i + 1) % data.length ) {
String val = String.valueOf(data[i]);
if ((i + 1) % data.length != tail) { //另一种写法i != tail -1
res.append( val + ", ");
}else {
res.append(val + "] tail. Size: " + String.valueOf(data[i]) + " capacity: " + String.valueOf(getCapacity()));
}
}
return res.toString();
}
public static void main(String[] args) {
// write your code here
LoopQueue<Integer> loopQueue = new LoopQueue<>();
for (int i = 0; i < 10; i ++) {
loopQueue.enQueue(i);
System.out.println(loopQueue);
if (i % 3 == 2) {
loopQueue.deQueue();
System.out.println(loopQueue);
}
}
}
}