你可以插秧,插花,插班,但不可以插队。
泱泱中华,礼仪大邦。无论在车站等车、去食堂打饭、去景点买门票等等,自觉排队已成为一个人的基本道德体现,井然有序的队列在生活中随处可见,今天忘忧跟大家一起讨论下程序世界的队列。
什么是队列
队列,是一种比较基础的数据结构,它属于线性数据结构中比较特殊的一种,他的特殊在于元素的进出受到了一定规则的限制,它需要时刻遵守先进先出(FIFO)的规则。即在买票过程中,越早进入队列的人越早买到票。
队列的实现
队列可以按照存储方式分为两种实现方式。
一种是基于数组实现的定长队列,它的长度取决于数组的长度。
另一种是基于列表实现的不定长队列,理论上可以无限长。
首先,使用接口定义队列应有的基本方法,代码如下:
/**
* algorithm
* @author 忘忧
* @date 2020/3/13
* 定义队列的基本方法
*/
public interface Queue {
/**
* 获取当前队列的长度
* @return 当前队列长度
*/
int size();
/**
* 判断当前队列是否已满
* @return 当前队列是否已满
*/
boolean isFull();
/**
* 元素出队
* @return 出队元素
* @throws QueueEmptyException 队列空时跑出该异常
*/
Object remove() throws QueueEmptyException;
/**
* 元素入队
* @param t 入队元素
* @throws QueueFullException 当队列满时抛出该异常
*/
void insert(Object t) throws QueueFullException;
}
基于数组实现的队列
思路:使用两个索引分别标记队列队首和队尾的位置。
队首元素出队时,将队首元素后移(如果后移后数组越界,则将队首元素置为0)。
新元素入队时,将队尾元素后移(如果后移后数组越界,则将队尾元素置为0)。
![](
基于数组的代码实现:
/**
* algorithm
* @author 忘忧
* @date 2020/3/13
* 队列相关知识
*/
public class ArrayQueue implements Queue {
private Object[] elements;
/**
* 队首元素位置
*/
private int headIndex = 0;
/**
* 队尾元素索引
*/
private int tailIndex = 0;
private int size = 0;
/**
* 初始化数组
* @param capacity 容量
*/
public ArrayQueue(int capacity) {
this.elements = new Object[capacity];
}
@Override
public int size() {
return this.size;
}
@Override
public boolean isFull() {
return this.size() == elements.length;
}
/**
* 队首元素出队
* @return 队首元素
* @throws QueueEmptyException 空队异常
*/
@Override
public Object remove() throws QueueEmptyException {
if(this.size() == 0){
throw new QueueEmptyException();
}
size--;
Object element = elements[headIndex++];
if(headIndex == elements.length){
headIndex = 0;
}
return element;
}
/**
* 新元素入队
* @param element 新元素
* @throws QueueFullException 满队异常
*/
@Override
public void insert(Object element) throws QueueFullException {
if(isFull()){
throw new QueueFullException();
}
size++;
elements[tailIndex++] = element;
if(tailIndex == elements.length){
tailIndex = 0;
}
}
}
基于链表实现的队列
思路:使用两个指针,分别标示头尾节点。
新增元素时,将尾节点的next置为新增节点,然后尾节点后移一位。
移除队首元素时,头节点后移一位。
需要留意的是,新增第一个元素和第二个元素需要特殊处理
- 由于新增第一个元素前,当前的头节点是null,需要将头节点设置为新增的节点
- 新增第二个节点时,由于头节点的next是null,需要将第二个节点设置为头节点的next)
/**
* algorithm
* @author 忘忧
* @date 2020/3/14
* 基于链表实现的队列
*/
public class LinkedQueue implements Queue {
static class TreeNode{
Object val;
TreeNode next;
}
/**
* 当前长度
*/
private int size;
/**
* 队首元素
*/
private TreeNode head;
/**
* 队尾元素
*/
private TreeNode tail;
@Override
public int size() {
return this.size;
}
@Override
public boolean isFull() {
return false;
}
@Override
public Object remove() throws QueueEmptyException {
if(this.size == 0){
throw new QueueEmptyException();
}
Object element = head.val;
head = head.next;
size--;
return element;
}
@Override
public void insert(Object element) throws QueueFullException {
TreeNode treeNode = new TreeNode();
treeNode.val = element;
//新增第一个元素的特殊处理
if(size == 0){
tail = treeNode;
head = treeNode;
}else if(size == 1){
//新增第二个原色的特水处理
tail.next = treeNode;
head.next = treeNode;
tail = treeNode;
}else{
tail.next = treeNode;
tail = treeNode;
}
size++;
}
}
队列的内容比较简单,关于队列,掌握两种实现方式的原理,基本上就足够应对大多数队列相关的算法了。
队列的主要操作只有入队和出队的操作,本篇忘忧实现的为非线程安全的队列,如果对多线程知识有所了解的话,可以尝试着自己实现一下线程安全的队列,在此忘忧就不过多讨论了。
扩展
- 双向队列
结束语:你不努力,就得出局。