一、定义
什么是队列?队列(queue)又叫先进先出表,它是一种运算受限的线性表。其限制是只允许在表的一端进行插入数据和另一端取数据。插入数据的一端是队尾,取数据的一端是队头。
队列的数据结构:数组或者链表实现
队列的常用场景:生产者生产数据放入队列缓存,消费者去消费数据。
二、循环队列
普通的队列往其中添加元素,队头指针和队尾指针分别指向队头和队尾,当队列中元素出队之后,队头指针和队尾指针指在了队尾,此时再有元素进来时会发生假溢出的情况。为此循环队列进行弥补。循环队列是指队头和队尾相连的队列,接下来我们用图来说明入队和出队整个过程。
假设循环队列总容量为N,并且预留一个空的位置作为队列空,满,长度判断标志:
队列空:front==rear;
队列满:(rear+1)%N==front;
队列元素个数:(rear – front + N)%N
三、队列的算法实现
通过上面的分析,队列的操作:队列大小,入队(队尾),出队(队头)
public class Queue<E> { Node<E> front; //始终指向 Node<E> head; //队头 Node<E> tail; //队尾 int QUEUE_SIZE; //模拟队列初始化大小 static final int DEFAULT_QUEUE_SIZE = 5; //设置默认队列大小 public Queue(){ this(DEFAULT_QUEUE_SIZE); } public Queue(int queueSize){ QUEUE_SIZE = queueSize + 1; //因为要空出一个节点,所以这里加1 checkQueueSize(); } /** * 入队,从队尾添加元素 */ public boolean push(E e){ if (head == tail) { Node<E> node = new Node(e,0, null); //入队,第一个节点 front = head = node; //更新队头 Node<E> rear = new Node(null, head.index + 1, null); //队尾 node.next = rear; //下一个节点是空队尾 tail = rear; //更新队尾指针 isClose(); } else { full(); //判断是否队满 if (tail.next != null) { //已经形成闭环 tail.e = e; tail = tail.next; } else { //还未形成闭环 Node<E> rear = new Node(null, tail.index + 1, null); //队尾 tail.e = e; //队尾添加元素 tail.next = rear; //添加新的空队尾 tail = rear; //更新队尾指针 isClose(); } } return true; } void isClose(){ if(size() == QUEUE_SIZE - 1) //相等则说明可以形成闭环,因为要留一个空节点,所以这里减1 tail.next = front; } /** * 出队,从队头取出元素 */ public E pop(){ if(empty()) return null; E e = head.e; head.e = null; //节点数据清空 head = head.next; //更新队头位置 return e; } /** * 获取队列中元素个数 * (tail – head + QUEUE_SIZE)%QUEUE_SIZE */ public int size(){ return (tail.index - head.index + QUEUE_SIZE) % QUEUE_SIZE; } /** * 队列是否为空 */ public boolean empty(){ return head == tail; } /** * 判断队列是否已满 * (rear+1)%N==front */ void full(){ if((tail.index + 1) % QUEUE_SIZE == head.index) throw new IndexOutOfBoundsException("队列已满,入队失败"); } /** * 初始化的队列大小检查 */ void checkQueueSize(){ if(QUEUE_SIZE < 1) throw new IndexOutOfBoundsException("QUEUE_SIZE : " + QUEUE_SIZE); } /** * 节点的定义 * @param <E> */ private static class Node<E>{ E e; //元素 int index; //当前节点的下标,从0开始直到QUEUE_SIZE Node<E> next; //下一个节点 Node(E e, int index, Node<E> next){ this.e = e; this.index = index; this.next = next; } } }
写到这里,队列通过单链表已经实现了。队列还可以通过数组来实现。