1. 基于数组实现循环队列
package com.feifei.demo.queue;
import java.io.Serializable;
import java.util.Objects;
import java.util.stream.Stream;
/**
* 自定义循环队列
* 1. 单队列会出现假溢出的情况,也就是队列有空闲空间,但是无法插入
* 队列有2个指针标识位,一个为front(用于指向队首) 一个为rear(用于指向队尾)
* 在队列为空时,front和rear相同
* front
* rear
* null null null null null
*
* 插入一个值后, rear后移一位, 只有当队列出队时,front才会发生偏移,请记住,
* 队列的队首一直指向队列的首部(有数据的首部)
* front
* rear
* 1 null null null null
*
* 当队列rear达到数组长度时,此时在出队一个数据,模型如下所示:
* front
* rear
* null 1 2 3 null
*
* 此时我们在插入数据5到队尾时,就会出现假溢出情况,明明还有存储空间,但是rear已经越界了
* front
* rear
* null 1 2 3 4 越界
*
* 此时,我们需要循环队列(逻辑上来说,实际还是数组)来完成此操作,当插入数据4时,我们的rear应该指
* 向数组索引下标为0的单元格,采用通用公式为 rear = (rear + 1) % capacity, 其中capacity为数
* 组长度此时模型如下所示:
* front
* rear
* null 1 2 3 4
*
* rear到达数组长度时,这里我们标识数组长度为capacity,此时我们再次插入数据,此时队列已满
* front
* rear
* 5 1 2 3 4
*
* 但是我们发现队列为空和队列已满时,都满足front == rear,为了区分,我们通常采用的做法是在队列还
* 剩一个空的单元格未插入时标识队列已满,比如:
* front
* rear
* null 1 2 3 4
*
* 这个就表示队列已满,满足front = (rear + 1) % capacity即标识队列已满
*
*/
public class CircularQueue<E> implements Serializable {
private static final long serialVersionUID = -2938153003420395845L;
/**
* 数据存储数组
*/
private transient Object[] elementData;
/**
* 定义队首
*/
private transient int front;
/**
* 定义队尾
*/
private transient int rear;
/**
* 定义元素个数
*/
private int size;
/**
* 自定义容量
*/
private int capacity;
/**
* 定义默认的数组容量大小, 定义为11的目的是为了实现预留一个存储空间来
* 区分队列是否已存满和队列是否为空, 因为队列为满和为空时的front == rear
*/
private transient static final int DEFAULT_CAPACITY_SIZE = 11;
public int size() {
return size;
}
public CircularQueue() {
elementData = new Object[(capacity = DEFAULT_CAPACITY_SIZE)];
}
public CircularQueue(int capacity) {
if (capacity <= 0) {
elementData = new Object[(this.capacity = DEFAULT_CAPACITY_SIZE)];
} else {
this.capacity = capacity;
elementData = new Object[(capacity + 1)];
}
this.size = 0;
}
/**
* 入队,数据添加至队尾
* 校验队列是否已满,因为队列为空
* @return
*/
public boolean offer(E element) {
if (Objects.isNull(element)) {
return false;
}
// 校验队列是否已满, 如果队列已满则不新增
if ((rear + 1) % capacity == front) {
System.out.println("队列已满, 不可再新增数据");
return false;
}
elementData[rear] = element;
size++;
// 将队尾重新赋值,这里不直接进行加一的原因在于可能会出现假溢出的情况
// 比如说已经存在队尾的索引为4,(4 + 1) % 5 = 0 => 队尾指向了索引下标为0
rear = (rear + 1) % capacity;
return true;
}
/**
* 出队,数据从队首中被移除
* @return
*/
public E poll() {
// 校验队列是否为空,为空则不允许删除
if (rear == front) {
System.out.println("队列为空,不允许删除");
return null;
}
E elementDatum = (E) elementData[front];
elementData[front] = null;
// 将队首重新赋值,赋值逻辑和队尾逻辑类似
front = (front + 1) % capacity;
// 将队列数据长度进行减一操作
size--;
return elementDatum;
}
public static void main(String[] args) {
CircularQueue<Integer> queue = new CircularQueue<>();
System.out.println("queue = " + queue.size());
System.out.println("queue = " + queue.capacity);
queue.offer(21);
queue.offer(22);
queue.offer(23);
queue.offer(24);
queue.offer(25);
queue.offer(26);
Object[] elementData = queue.elementData;
Stream.of(elementData).forEach(ele -> System.out.println(ele));
System.out.println("elementData = " + queue.poll());
System.out.println("elementData = " + queue.poll());
System.out.println("elementData = " + queue.poll());
Stream.of(queue.elementData).forEach(ele -> System.out.println(ele));
}
}
2. 使用双向链表实现队列:
package com.feifei.demo.queue;
/**
* 自定义链表队列(链表为双向链表),理论上是没有长度限制的
*/
public class LinkedQueue<E> {
/**
* 定义队首
*/
private Node<E> front;
/**
* 定义队尾
*/
private Node<E> rear;
/**
* 定义数据长度
*/
private int size;
/**
* 定义节点类
* @param <E>
*/
private class Node<E> {
private E element;
private Node<E> preNode;
private Node<E> nextNode;
}
public LinkedQueue() {
front = rear = new Node<>();
}
/**
* 获取数据长度
* @return
*/
public int size() {
return size;
}
/**
* 入队
*
* @param element
* @return
*/
public boolean offer(E element) {
Node<E> eNode = new Node<>();
eNode.element = element;
// 当队列为空时
if (rear == front) {
front = eNode;
rear.preNode = eNode;
} else {
Node<E> preNode = rear.preNode;
preNode.nextNode = eNode;
eNode.preNode = preNode;
rear.preNode = eNode;
System.out.println("添加成功");
}
size++;
return true;
}
/**
* 出队
*
* @return
*/
public E pop() {
if (rear == front) {
System.out.println("队列为空,无法进行删除操作");
}
E element = front.element;
Node<E> nextNode = front.nextNode;
// 置空处理,方便GC回收
front.preNode = null;
front.nextNode = null;
front.element = null;
// 将下一个节点作为队首
front = nextNode;
size--;
return element;
}
public static void main(String[] args) {
LinkedQueue<Integer> linkedQueue = new LinkedQueue<>();
linkedQueue.offer(1);
linkedQueue.offer(2);
linkedQueue.offer(3);
linkedQueue.offer(4);
linkedQueue.offer(5);
System.out.println("linkedQueue pop = " + linkedQueue.pop());
System.out.println("linkedQueue pop = " + linkedQueue.pop());
System.out.println("linkedQueue pop = " + linkedQueue.pop());
System.out.println("linkedQueue pop = " + linkedQueue.pop());
System.out.println("linkedQueue pop = " + linkedQueue.pop());
}
}