目录
1、队列的介绍
计算机科学中,queue 是以顺序的方式维护的一组数据集合,在一端添加数据,从另一端移除数据。习惯来说,添加的一端称为尾,移除的一端称为头,就如同生活中的排队买商品。
在 JDK 中,自带的 队列接口为 java.util.Queue 如下。
在 队列中常用的方法有 offer、peek 等等。
2、自定义队列接口
在自定义队列接口中,定义了对队列操作的常见的几个抽象方法,如 offer(向队尾添加一个元素)、poll(移除并返回队列头部第一个元素)等等。
在这里使用了泛型 <E> ,可以向队列中操作不同类型的数据。
/**
* 队列基础接口
*
* @author zjj_admin
*/
public interface Queue<E> {
/**
* 向队列的尾部插入数据
*
* @param value 添加数据
* @return 是否添加成功
*/
boolean offer(E value);
/**
* 从队列的头部拉取一个数据,并从队列中移除
*
* @return 返回头部数据,没有数据时返回 null
*/
E poll();
/**
* 获取队列头部数据,不移除数据
*
* @return
*/
E peek();
/**
* 判断队列是否为空
*
* @return 队列是否为空
*/
boolean isEmpty();
/**
* 队列是否已经满了
*
* @return 队列是否已满
*/
boolean isFull();
/**
* 获取队列中元素的个数
*
* @return 元素个数
*/
int size();
}
3、基于单项循环链表(带哨兵)实现队列结构
实现步骤:
-
基于链表实现,定义链表的基础结构 Node ;
-
声明链表的头(head)和尾(tail),在这里,head 为链表的哨兵节点,不存储数据;tail 节点为尾结点,用于存储链表的最后一个节点数据,tail.next = head;
-
声明队列的元素个数(size)和队列的容量(capacity);
-
实现队列中声明的每一个抽象方法即可;
-
让自定义队列实现 Iterable 接口,使其可以使用增强 for 循环遍历。
具体的代码实现
/**
* 使用单向环形链表(带哨兵)的数结构实现队列
*
* @author zjj_admin
*/
public class LinkedListQueue<E> implements Queue<E>, Iterable<E> {
/**
* 队列中链表的数据结构
*
* @param <E>
*/
private static class Node<E> {
/**
* 当前节点数据
*/
E value;
/**
* 下一个节点
*/
Node<E> next;
public Node(E value, Node<E> next) {
this.value = value;
this.next = next;
}
}
/**
* 头结点,也是哨兵节点,不存储真实数据
*/
private Node<E> head = new Node<>(null, null);
/**
* 队尾,存储真实节点数据
*/
private Node<E> tail = head;
/**
* 队列的数据个数
*/
private int size;
/**
* 队列的容量大小
*/
private int capacity = Integer.MAX_VALUE;
public LinkedListQueue(int capacity) {
//调用无参构造,必须放在第一行
this();
if (capacity < 0) {
throw new IndexOutOfBoundsException("capacity 不能小于 0");
}
this.capacity = capacity;
}
/**
* 构造方法
*/
public LinkedListQueue() {
head.next = tail;
tail.next = head;
this.size = 0;
}
/**
* 添加步骤
* 1、创建一个新节点,让新节点的 next 指针指向头结点
* 2、让 tail 的 next 指针指向 新添加的节点
* 3、让 tail 指向 新添加的节点即可。
*
* @param value 添加数据
* @return
*/
@Override
public boolean offer(E value) {
if (isFull()) {
return false;
}
Node<E> added = new Node<>(value, head);
tail.next = added;
tail = added;
size++;
return true;
}
@Override
public E poll() {
if (isEmpty()) {
return null;
}
Node<E> first = head.next;
//移除 first
head.next = first.next;
//当 first 是尾结点时,让新的 tail 指向 head
if (first == tail) {
tail = head;
}
size--;
return first.value;
}
@Override
public E peek() {
if (isEmpty()) {
return null;
}
return head.next.value;
}
@Override
public boolean isEmpty() {
return head == tail;
}
@Override
public boolean isFull() {
return size == capacity;
}
@Override
public int size() {
return size;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
Node<E> p = head.next;
@Override
public boolean hasNext() {
return p != head;
}
@Override
public E next() {
E value = p.value;
p = p.next;
return value;
}
};
}
}
4、基于环形数组实现队列结构
使用环形数组的好处
-
对比普通数组,起点和终点更为自由,不用考虑数据移动
-
“环”意味着不会存在【越界】问题
-
数组性能更佳
-
环形数组比较适合实现有界队列、RingBuffer 等
具体的代码实现
/**
* 基于环形数组实现队列
* 环形数组有 head 和 tail ,head 表示第一个真实数据,tail 不存储数据,表示队列的尾部
* 使用环形数组的好处是,任何一盒位置都可以作为新的 head 和 tail
*
* @author zjj_admin
*/
public class RingArrayQueue1<E> implements Queue<E>, Iterable<E> {
/**
* 队列元素
*/
private final E[] array;
/**
* 头结点的索引
*/
private int head;
/**
* 尾结点的索引
*/
private int tail;
/**
* 队列元素个数
*/
private int size;
/**
* 队列的容量,最大值为 1000
*/
private int capacity = 10000;
{
// 代码块,初始化数据
head = 0;
tail = 0;
size = 0;
}
@SuppressWarnings("all")
public RingArrayQueue1(int capacity) {
if (capacity < 0 || capacity > 10000) {
throw new IndexOutOfBoundsException("capacity 不符合规范");
}
this.capacity = capacity;
//初始化数组,多余的一个用于存放 tail 指针
array = (E[]) new Object[capacity + 1];
}
@SuppressWarnings("all")
public RingArrayQueue1() {
array = (E[]) new Object[capacity + 1];
}
/**
* 添加新节点,就将当前的 tail 位置存储数据,并且将 tail 向后移动一位
*
* @param value 添加数据
* @return
*/
@Override
public boolean offer(E value) {
if (isFull()) {
return false;
}
array[tail] = value;
// 将尾结点向前移动一位
tail = (tail + 1) % array.length;
size++;
return true;
}
/**
* 移除第一个节点,获取当前 head 节点的数据,并将 head 节点向后移动一位
*
* @return
*/
@Override
public E poll() {
if (isEmpty()) {
return null;
}
E value = array[head];
// 将头结点向前移动一位
head = (head + 1) % array.length;
size--;
return value;
}
@Override
public E peek() {
if (isEmpty()) {
return null;
}
return array[head];
}
@Override
public boolean isEmpty() {
return size == 0;
}
/**
* 这里当队列满时
*
* @return
*/
@Override
public boolean isFull() {
return size == array.length - 1;
}
@Override
public int size() {
return size;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int index = head;
int count = 0;
@Override
public boolean hasNext() {
return count < size;
}
@Override
public E next() {
E value = array[index];
index = (index + 1) % array.length;
count++;
return value;
}
};
}
}