
队列(Queue)是一种重要的线性数据结构,遵循先进先出、后进后出的原则。本文将更详细地介绍队列的概念、特点、Java 实现以及应用场景。
模运算小复习:
a % b 的值总是小于b
5 % 4 = 1 5 % 2 = 1
1 % 5 = 1 4 % 5 = 4
想象一下排队买票,先排队的人总是先买到票。队列就像这样,元素从一端进入,称为队尾(Rear)或尾指针(tail),从另一端取出,称为队首(Front)或头指针(head)。元素的添加操作称为入队(Enqueue)或加入队列,删除操作称为出队(Dequeue)或移出队列。

队列是一种抽象数据类型 (ADT),这意味着我们只关心它的操作和特性,而不关心具体的实现方式。队列的关键操作包括:
Java 中可以使用链表或环形数组实现队列。

public class LinkedListQueue<T> {
private Node<T> head; // 指向队首节点的指针
private Node<T> tail; // 指向队尾节点的指针
private int size; // 记录队列大小
private static class Node<T> { // 链表节点的内部类
T data;
Node<T> next;
Node(T data) {
this.data = data;
}
}
public LinkedListQueue() {
this.head = null;
this.tail = null;
this.size = 0;
}
public boolean isEmpty() {
return size == 0; // 或 head == null
}
public int size() {
return size;
}
//入队
public void enqueue(T item) {
Node<T> newNode = new Node<>(item);
if (isEmpty()) {
head = newNode; // 如果队列为空,新节点既是队首也是队尾
} else {
tail.next = newNode; // 否则,将新节点添加到队尾
}
tail = newNode; // 更新队尾指针
size++;
}
//出队
public T dequeue() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty"); // 使用更具体的异常类型
}
T data = head.data;
head = head.next; // 更新队首指针
if (head == null) { // 如果队列只有一个元素,出队后队列变空,tail 也需要置空
tail = null;
}
size--;
return data;
}
//返回队首元素
public T peek() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty");
}
return head.data;
}
}
好处
public class ArrayQueue<T> {
private T[] data;
private int head; // 队首指针
private int tail; // 队尾指针
private int capacity; // 数组容量
private int size; // 队列大小
public ArrayQueue(int capacity) {
this.capacity = capacity;
this.data = (T[]) new Object[capacity];
this.head = 0;
this.tail = 0;
this.size = 0;
}
public boolean isEmpty() {
return size == 0; // 或 head == tail
}
public boolean isFull() {
return size == capacity; // 使用 size 判断是否满
}
public int size() { return size; }
//入队
public void enqueue(T item) {
if (isFull()) {
//throw new RuntimeException("Queue is full");
resizeArray(); // 扩容操作
}
data[tail] = item;
tail = (tail + 1) % capacity; // 环形数组的关键:使用模运算
size++;
}
//出队
public T dequeue() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty");
}
T item = data[head];
data[head] = null; // 避免对象游离
head = (head + 1) % capacity; // 环形数组的关键:使用模运算
size--;
return item;
}
//返回队首元素
public T peek() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty");
}
return data[head];
}
private void resizeArray() { // 扩容方法示例
int newCapacity = capacity * 2;
T[] newData = (T[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[(head + i) % capacity];
}
data = newData;
head = 0;
tail = size;
capacity = newCapacity;
}
}例如,数组长度是 5,当前位置是 3 ,向前走 2 步,此时下标为 (3 + 2) % 5 = 0

引入size属性后有两种方式:
return tail == size; //队列大小
return size == 0;
引入size属性后有两种方式:
return size = capacity; //数组容量
return (tail + 1) % length == head;
假设入队前队空:此时head == tail

入队后:head 不变,tail + 1,代码中这样书写 :tail = (tail + 1) % length,保证了不会越界的情况,不能设置为 tail ++ 。此时a即是队头也是队尾。

这里要牢记队列的特点:先进先出、后进后出
假设此时a、b已入队,现在a要出队,出队前:a是队头,b是队尾

a出队后:此时b成为新队头

略,具体实现:首先确保不是空队列,然后返回 data[head]
队列是一种简单但强大的数据结构,其 FIFO 特性使其在许多场景下都非常有用,例如:
理解队列的概念和实现对于程序员来说至关重要,它能帮助我们更好地设计和优化程序。
希望本文能帮助各位看官更好地理解队列这种重要的数据结构!下期见,谢谢~