循环队列数据结构是什么样的?
循环结构底层实现:数组
数据控制:由指针控制数据进出。
怎么循环?:看代码 模糊的说就是根据指针把空间填满。一般队列是数据出去后前面的空间就为空,而循环队列就是将那些空出来的空间进行循环使用。
实现接口:
public class ArrayQueueLoop1<E> implements Queue<E> {
public E[] data; // 定义数组
private int front; // 定义头指针: 也可以叫出队指针
private int rear; // 定义尾指针 进队指针
private int size; // 定义数据长度
final private static int DEFAULT_LENGTH = 10;// 定义默认长度
// 定义构造方法 一个默认 一个有参 分别定义一个默认长度的循环队列 和一个 指定长度的循环队列
public ArrayQueueLoop1() {
data = (E[]) new Object[DEFAULT_LENGTH+1];
front = 0;
rear = 0;
size = 0;
}
public ArrayQueueLoop1(int capacity) {
data = (E[]) new Object[capacity+1];
front = 0;
rear = 0;
size = 0;
}
//只能進行擴容 縮容不行
// public void resize(int newLength) {
// E[] newE = (E[]) new Object[newLength+1];
// int k = 0;
// for (int i = 0; i < getSize(); i++) {
//
// if (rear - front < 0) {
// for (int j = 0; j < rear; j++) {
// newE[k] = data[i];
// k++;
// }
// }
//
// newE[k] = data[i];
// k++;
//
// }
// front = 0;
// rear = getSize();
// data = newE;
//
// }
public void resize(int newLen) {
// 扩容:怎么扩?创建一个新的数组,从出栈指针开始进行遍历,将元素放进新数组,
//如果进栈指针减去出栈指针是负数,那就证明出栈指针前面也是有元素的,
//并且是它自身后面的元素,那么从0开始,把进栈指针之前的元素也复制进新数组中,最后替换数组,并重新设置指针。
E[] newData = (E[]) new Object[newLen+1];
int index = 0;// 新数组角标
for (int i = front; i != rear; i = (i + 1) % data.length) {
newData[index++] = data[i];
}
front = 0;
rear = index;
data=newData;
}
// 扩容另一种实现方法
@Override
public int getSize() {
// TODO Auto-generated method stub
// 计算循环队列元素个数:n=(rear-front+ MAXSIZE) mod MAXSIZE 这个公式 有两种情况 头在前
// 尾在后,头减去尾就可以计算出 元素个数 第二种情况 尾在前头在后,那么尾数减去头的话就是负数 依然代表元素个数
// 加上总长度,求余总长度,巧妙的公式
return (rear - front + data.length) % data.length;
//return size;
}
@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
// 非空判断 怎么判断?进队列指针==出队列指针
// 就肯定为空了,还有另一种情况,出队列指针永远指向一个空的元素空间。这样就要是出队指针+1==进队指针
return front + 1 == rear;
}
@Override
public void clear() {
// TODO Auto-generated method stub
// 清除数据
// 进队指针==0;出队指针==0;数据长度==0;这样就清零了
front = 0;
rear = 0;
size = 0;
}
@Override
public void enqueue(E e) {
// TODO Auto-generated method stub
// 入队 1.容量判断
if (size>(data.length * 0.7)) {
// 当数组中元素个数大于等于数组容量的7%时,我们就要进行扩容了
resize((int)(data.length-1) * 2);// 定义一个扩容方法
}
data[rear] = e;// 将传进来的参数赋给进队指针rear的空间。因为我们的进队指针和出队指针同时指向0;所以只需将进队指针+1
rear++; // 进队指针++
size++; // 元素个数++;
}
@Override
public E dequeue() {
// TODO Auto-generated method stub
// 出队 依照出队规则 我们需要将第一个进来的数据返回出去 需要进行缩容判断
if (size <(data.length/4)) {
System.out.println("進入縮容方法");
resize((data.length/2));
}
front++;
size--;
return data[front]; // 返回出去后,我们的出队指针就要指向第二个进队的元素 所以front++;
}
@Override
public E getFront() {
// TODO Auto-generated method stub
// 获取队首元素 不删除 在出队与入队过程中,我们的指针是变化的所以直接返回出队指针即可 进行非空验证
if (isEmpty()) {
throw new NullPointerException("队列为空");
}
return data[front];
}
@Override
public E getRear() {
// TODO Auto-generated method stub
// 获取队尾元素 不删除 与取队首原理相同,直接取队尾的元素,记得减一 因为我们的队尾指针指向的是一个空元素
return data[rear - 1];
}
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){
sb.append(data[i]);
if((i+1)%data.length==rear){
sb.append(']');
}else{
sb.append(',');
}
}
}
return sb.toString();
}
}
简单的实现这几个方法,如果还需要实现什么方法可以自己加。纯手打,有错欢迎批评指正。