2.4 隊(duì)列
概述
計(jì)算機(jī)科學(xué)中,queue 是以順序的方式維護(hù)的一組數(shù)據(jù)集合,在一端添加數(shù)據(jù),從另一端移除數(shù)據(jù)。習(xí)慣來說,添加的一端稱為尾,移除的一端稱為頭,就如同生活中的排隊(duì)買商品
In computer science, a queue is a collection of entities that are maintained in a sequence and can be modified by the addition of entities at one end of the sequence and the removal of entities from the other end of the sequence
先定義一個(gè)簡化的隊(duì)列接口
public interface Queue<E> {
/**
* 向隊(duì)列尾插入值
* @param value 待插入值
* @return 插入成功返回 true, 插入失敗返回 false
*/
boolean offer(E value);
/**
* 從對(duì)列頭獲取值, 并移除
* @return 如果隊(duì)列非空返回對(duì)頭值, 否則返回 null
*/
E poll();
/**
* 從對(duì)列頭獲取值, 不移除
* @return 如果隊(duì)列非空返回對(duì)頭值, 否則返回 null
*/
E peek();
/**
* 檢查隊(duì)列是否為空
* @return 空返回 true, 否則返回 false
*/
boolean isEmpty();
/**
* 檢查隊(duì)列是否已滿
* @return 滿返回 true, 否則返回 false
*/
boolean isFull();
}
鏈表實(shí)現(xiàn)
下面以單向環(huán)形帶哨兵鏈表方式來實(shí)現(xiàn)隊(duì)列

image.png

image.png

image.png
代碼
public class LinkedListQueue<E>
implements Queue<E>, Iterable<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 = 0;
private int capacity = Integer.MAX_VALUE;
{
tail.next = head;
}
public LinkedListQueue() {
}
public LinkedListQueue(int capacity) {
this.capacity = capacity;
}
@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;
head.next = first.next;
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 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;
}
};
}
}
環(huán)形數(shù)組實(shí)現(xiàn)
好處
- 對(duì)比普通數(shù)組,起點(diǎn)和終點(diǎn)更為自由,不用考慮數(shù)據(jù)移動(dòng)
- “環(huán)”意味著不會(huì)存在【越界】問題
- 數(shù)組性能更佳
- 環(huán)形數(shù)組比較適合實(shí)現(xiàn)有界隊(duì)列、RingBuffer 等

image.png
下標(biāo)計(jì)算
例如,數(shù)組長度是 5,當(dāng)前位置是 3 ,向前走 2 步,此時(shí)下標(biāo)為

image.png
- cur 當(dāng)前指針位置
- step 前進(jìn)步數(shù)
- length 數(shù)組長度
注意:
- 如果 step = 1,也就是一次走一步,可以在 >= length 時(shí)重置為 0 即可
判斷空

image.png
判斷滿

image.png
滿之后的策略可以根據(jù)業(yè)務(wù)需求決定
- 例如我們要實(shí)現(xiàn)的環(huán)形隊(duì)列,滿之后就拒絕入隊(duì)
代碼
public class ArrayQueue<E> implements Queue<E>, Iterable<E>{
private int head = 0;
private int tail = 0;
private final E[] array;
private final int length;
@SuppressWarnings("all")
public ArrayQueue(int capacity) {
length = capacity + 1;
array = (E[]) new Object[length];
}
@Override
public boolean offer(E value) {
if (isFull()) {
return false;
}
array[tail] = value;
tail = (tail + 1) % length;
return true;
}
@Override
public E poll() {
if (isEmpty()) {
return null;
}
E value = array[head];
head = (head + 1) % length;
return value;
}
@Override
public E peek() {
if (isEmpty()) {
return null;
}
return array[head];
}
@Override
public boolean isEmpty() {
return tail == head;
}
@Override
public boolean isFull() {
return (tail + 1) % length == head;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int p = head;
@Override
public boolean hasNext() {
return p != tail;
}
@Override
public E next() {
E value = array[p];
p = (p + 1) % array.length;
return value;
}
};
}
}
判斷空、滿方法2
引入 size
public class ArrayQueue2<E> implements Queue<E>, Iterable<E> {
private int head = 0;
private int tail = 0;
private final E[] array;
private final int capacity;
private int size = 0;
@SuppressWarnings("all")
public ArrayQueue2(int capacity) {
this.capacity = capacity;
array = (E[]) new Object[capacity];
}
@Override
public boolean offer(E value) {
if (isFull()) {
return false;
}
array[tail] = value;
tail = (tail + 1) % capacity;
size++;
return true;
}
@Override
public E poll() {
if (isEmpty()) {
return null;
}
E value = array[head];
head = (head + 1) % capacity;
size--;
return value;
}
@Override
public E peek() {
if (isEmpty()) {
return null;
}
return array[head];
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean isFull() {
return size == capacity;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int p = head;
@Override
public boolean hasNext() {
return p != tail;
}
@Override
public E next() {
E value = array[p];
p = (p + 1) % capacity;
return value;
}
};
}
}
判斷空、滿方法3
-
head 和 tail 不斷遞增,用到索引時(shí),再用它們進(jìn)行計(jì)算,兩個(gè)問題
如何保證 head 和 tail 自增超過正整數(shù)最大值的正確性
如何讓取模運(yùn)算性能更高
答案:讓 capacity 為 2 的冪
public class ArrayQueue3<E> implements Queue<E>, Iterable<E> {
private int head = 0;
private int tail = 0;
private final E[] array;
private final int capacity;
@SuppressWarnings("all")
public ArrayQueue3(int capacity) {
if ((capacity & capacity - 1) != 0) {
throw new IllegalArgumentException("capacity 必須為 2 的冪");
}
this.capacity = capacity;
array = (E[]) new Object[this.capacity];
}
@Override
public boolean offer(E value) {
if (isFull()) {
return false;
}
array[tail & capacity - 1] = value;
tail++;
return true;
}
@Override
public E poll() {
if (isEmpty()) {
return null;
}
E value = array[head & capacity - 1];
head++;
return value;
}
@Override
public E peek() {
if (isEmpty()) {
return null;
}
return array[head & capacity - 1];
}
@Override
public boolean isEmpty() {
return tail - head == 0;
}
@Override
public boolean isFull() {
return tail - head == capacity;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int p = head;
@Override
public boolean hasNext() {
return p != tail;
}
@Override
public E next() {
E value = array[p & capacity - 1];
p++;
return value;
}
};
}
}