循环队列
队列的头尾相连的顺序结构称为循环队列
1. 为何有循环队列
队列的不足
每次出队的时候,队首后面的元素都得一个一个往前移一位,保证队首在下标为0处
插入的时候,插入位置后面元素都得往后移动一位
时间复杂度为O(n)
2.解决办法
不去移动元素,而去移动元素所在下标,将队列目前第一个元素所在角标作为队首
定义头指针front,与尾指针rear去进行移动
初始条件头尾指针都位于0处,插入时rear指针向后移动,出队时front指针也向后移动
但当rear移动到队尾时再进行后移时将移动到?
当reae指针移到队尾时,若队首还有位置则跳到队首,将队尾与队首相连
入队出队操作,当rear=front时队满,进行扩容
3.循环队列的问题
队满时 front = rear
队空时 front = rear
满和空的条件都一样,如何判断
4.其中一种解决办法
front指针指向队首,rear指针指向队尾后一个位置,留出一个null的位置作为标志位
队满条件:(rear+1)%data.length ==front
队空条件:rear==front
时间复杂度变为O(1)
5.代码实现方法
1. 定义及构造方法
/**
* 循环队列的顺序结构的实现
* @author TZH
* @param <E>
*/
public class ArrayQueueLoop<E> implements Queue<E> {
private E[] data; //泛型数组
private int front; //头指针
private int rear; //尾指针
private int size; //元素个数
private static int SIZE = 10; //默认容量
//指定容量的循环队列
public ArrayQueueLoop(int capaticy) {
data = (E[]) new Object[capaticy+1];
/**
* +1主要是因为尾指针后还预留了一个null的位置,便于确定元素容量满的状态
* 头尾指针默认都从0处开始,元素个数初始为0
*/
front = 0;
rear = 0;
size = 0;
}
//默认容量的循环队列
public ArrayQueueLoop (){
this(SIZE);
}
2.得到元素个数,清空,判空方法
public int getSize() {
return size;
}
public boolean isEmpty() {
return front==rear&&size==0;
}
public void clear() {
front=0;
rear=0;
size=0;
}
3.入队
/**入队
*
*/
public void enqueue(E e) {
if((rear+1)%data.length==front){
resize(data.length*2-1); //扩容
}
data[rear] = e; //null所在位置为新元素入队位置
rear = (rear+1)%data.length; //尾指针右移一位
size++;
}
4.出队
/**
* 出队
*/
public E dequeue() {
if(isEmpty()){
throw new NullPointerException("循环队列为空");
}
E e = data[front]; //出队元素
front = (front+1)%data.length; //头指针向右移一位
size--; //元素个数减一
if(data.length>SIZE+1&&size<=data.length/4){
//长度大于默认容量+1,留有一个空闲null
//元素个数<=长度的1/4
resize(data.length/2+1);
}
return e;
}
5.容量变化过程
/**
* 容量变化过程
* @param i
*/
@SuppressWarnings("unchecked")
private void resize(int newLen) {
E[] newdata = (E[]) new Object[newLen];
//创建一个数组用来存放扩容前元素,向下转型
int index = 0; //标记取出元素个数
for(int i=front;i!=rear;i=(i+1)%data.length){
/*
* 从头指针开始取出元素
* 当取到尾指针的位置时退出循环
* 取出一个i往后移动,不能直接++,对length取余得出所在位置
*/
newdata[index++] = data[i]; //赋值且index向右移动
}
front = 0; //头指针重新为0
rear = index; //扩容后的尾指针为index值
data = newdata; //更新数组
}
6.得到队尾队首元素
/**
* 得到队首元素,不出队
*
*/
public E getFront() {
return data[front];
}
/**
* 得到队尾元素,不出队
*/
public E getRear() {
// 元素456 123
// 角标0123456
// 7+3-1=9%7=2
return data[(data.length+rear-1)%data.length];
}
7.toString()方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ArrayQueueLoop size="+getSize()+",capacity ="+(data.length-1)+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append('[');
for(int i=front;i!=rear;i=(i+1)%data.length){
//i为取出元素时的指针
//i=rear的时候循环退出
//当i+1取余,而不是i++
sb.append(data[i]);
if((i+1)%data.length==rear){
//当i与尾指针重合时取出的是最后一个元素
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
}
8.equals()
@Override
public boolean equals(Object obj) {
if(obj==null){
return false;
}
if(obj==this){
return true;
}
if(obj instanceof ArrayQueueLoop ){
ArrayQueueLoop queue = (ArrayQueueLoop) obj;
if(queue.getSize()==getSize()){
for(int i=front;i!=rear;i=(i+1)%data.length){
if(data[i]!=queue.data[i]){
return false;
}
}
// for(int i=0;i<getSize();i++){
// if(queue.dequeue()!=dequeue()){
// return false;
// }
// }
return true;
}
}
return false;
}