什么是循环队列
队列是一种特殊的线性表,循环队列是将向量空间想象为一个首尾相接的圆环。
1、队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。
2、循环队列是将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列。 在顺序队列中,当队尾指针已经到数组的上界,不能再有入队操作,但其实数组中还有空位置,这就叫做“假溢出”,解决假溢出的途径----采用循环队列。
实现一个循环队列要注意的点
- 队空时,头尾指针应该相等
- 队满时,头指针应该正好在尾指针后一位
- 入队时,尾指针加一
- 出队时,头指针加一
- 对数组长度取模是因为是循环队列,并防止溢出
代码实现
/**
* 循环队列
*/
public class CircularQueue {
// 声明数组
int[] array;
// 元素的个数
int elementSize;
// 头指针
int front;
// 尾指针
int tail;
// [0,1,2,3,4,5] 元素加一,用于循环队列,判断数组是否已经满了
public CircularQueue(int c){
array = new int[c + 1];
elementSize = 0;
front = 0;
tail = 0;
}
// 给队列一个初始的长度
public CircularQueue(){
this(5);
}
// 获取数组中的元素,把原来加的一再减去
public int getSizeEle(){
return array.length - 1;
}
// 当头指针的下标索引等于尾指针的下标索引时,队列为空
public boolean isEmpty(){
return front == tail;
}
// 获取元素的个数
public int getSize(){
return elementSize;
}
/**
* 添加元素
* @param e
*/
public void pushElement(int e){
// 考虑数组为空和队列已经满的情况,这里浪费一个元素空位,判断数组是否扩容
/**
* 示例:
* 往数组添加元素:
* [1,2,3,4,5,6,7]
* 取出一个元素来:
* [0,2,3,4,5,6,7] 那当前的tail的下标索引就应该是0,没有元素,
* 而front因为取出了一个元素,那下标指针就指向了1,然后tail+1取模,触发扩容机制
* 而,实际上现在0的索引位置是没有值的
*/
if ((tail+1) % array.length == front){
resize(getSizeEle() * 2);
}
// 添加元素
array[tail] = e;
// 循环队列,防止下标越界
tail = (tail + 1) % array.length;
elementSize ++;
}
/**
* 取出元素
* @return
*/
public int popElement(){
if (isEmpty()){
throw new RuntimeException("队列为空!");
}
// 用于返回
int value= array[front];
// 取出值,默认为0
array[front] = 0;
// 下标向后移动一位
front = (front + 1) % array.length;
elementSize --;
// 如果开辟的空间大,元素比较少,就进行缩容
if (elementSize == getSizeEle() / 4 && getSizeEle() / 2 != 0){
resize(getSizeEle() / 2);
}
return value;
}
/**
* 扩容机制
* @param cap
*/
private void resize(int cap) {
int[] newData = new int[cap];
for (int i = 0; i < elementSize; i++) {
/**
* 由于是循环队列,所以新数组和本来数组的下标是不对应的
* 例如:
* array : [0,0,0,3,4,5,6,7]
* newDate : [0+front,1+front,2+front,...]
*/
newData[i] = array[(i+front) % array.length];
}
// 把新数组重新指回
array = newData;
front = 0;
tail = elementSize;
}
public static void main(String[] args) {
CircularQueue queue = new CircularQueue();
for(int i = 0 ; i < 15 ; i ++){
// 添加元素,不够扩容
queue.pushElement(i+1);
// 每三个元素,出栈一次
if(i % 3 == 2){
int i1 = queue.popElement();
System.out.println(i1);
}
}
}
}
LeetCode版循环队列实现
public class TestCode {
int[] data;
int size;
int front;
int tail;
public TestCode(int k){
data = new int[k + 1];
size = 0;
front = 0;
tail = 0;
}
public int Front(){
if (isEmpty()){
return -1;
}
return data[front];
}
public int Rear(){
if (isEmpty()){
return -1;
}
if (tail == 0 && getSize() != 0){
return data[getSize()];
}
int tailIndex = (tail - 1) % data.length;
return data[tailIndex];
}
public boolean isEmpty(){
return size == 0;
}
public int getSize(){
return data.length - 1;
}
public boolean isFull(){
if (getSize() == size){
return true;
}
return false;
}
public boolean enQueue(int value){
if (isFull()){
return false;
}
data[tail] = value;
tail = (tail + 1) % data.length;
size ++;
return true;
}
public boolean deQueue(){
if (isEmpty()){
return false;
}
front = (front + 1) % data.length;
size --;
return true;
}
public static void main(String[] args) {
// ["TestCode","enQueue","enQueue","enQueue","enQueue","Rear","isFull","deQueue","enQueue","Rear"]
// [[3],[1],[2],[3],[4],[],[],[],[4],[]]
// 输出
// [null,true,true,true,false,3,true,true,true,4]
// 预期结果
// [null,true,true,true,false,3,true,true,true,4]
TestCode obj = new TestCode(3);
for (int i = 0; i < 1; i++) {
boolean param_1 = obj.enQueue(1);
boolean param_2 = obj.enQueue(2);
boolean param_3 = obj.enQueue(3);
boolean param_4 = obj.enQueue(4);
int param_5 = obj.Rear();
boolean param_6 = obj.isFull();
boolean param_7 = obj.deQueue();
boolean param_8 = obj.enQueue(4);
int param_9 = obj.Rear();
int param_10 = obj.Front();
System.out.println(param_1);
System.out.println(param_2);
System.out.println(param_3);
System.out.println(param_4);
System.out.println(param_5);
System.out.println(param_6);
System.out.println(param_7);
System.out.println(param_8);
System.out.println(param_9);
System.out.println(param_10);
}
}
}