数据结构中的队列(Queue)是一种常见且重要的数据结构,它遵循先进先出(FIFO, First-In-First-Out)的原则。在Java中,队列可以通过多种方式实现,如使用数组、链表等。
一、队列的基本概念
队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。队列中的元素按照它们被插入的顺序排列,即先插入的元素会被先删除。队列的主要操作包括入队(enqueue)、出队(dequeue)、查看队头元素(peek/front)以及判断队列是否为空(isEmpty)等。
二、队列的特性
- 先进先出(FIFO):队列按照元素被插入的顺序进行排序,先插入的元素会先被删除。
- 限制性访问:队列只允许在队尾插入元素,在队头删除元素,不支持随机访问队列中的元素。
- 动态扩容:虽然在实际应用中队列的大小通常是有限的,但某些实现(如Java中的
LinkedList
)可以动态地调整其容量以存储更多的元素。
三、Java中的队列实现
在Java中,队列可以通过多种方式实现,包括使用Java集合框架(Java Collections Framework)中的Queue
接口及其实现类,如LinkedList
、PriorityQueue
等。
import java.util.LinkedList;
import java.util.Queue;
public class QueueExample {
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>();
// 入队
queue.offer(1);
queue.offer(2);
queue.offer(3);
// 查看队头元素
System.out.println("队头元素: " + queue.peek()); // 输出: 1
// 出队
System.out.println("出队元素: " + queue.poll()); // 输出: 1
// 判断队列是否为空
System.out.println("队列是否为空: " + queue.isEmpty()); // 输出: false
}
}
1. 使用LinkedList
实现队列
LinkedList
是一个双向链表,但它也实现了Queue
接口,因此可以用作队列。LinkedList
提供了offer()
方法用于入队,poll()
方法用于出队,peek()
方法用于查看队头元素而不删除它,以及isEmpty()
方法用于检查队列是否为空。
import java.util.LinkedList;
public class QueueWithLinkedList {
private LinkedList<Integer> list = new LinkedList<>();
// 入队操作
public void enqueue(int element) {
list.addLast(element); // 使用addLast()方法将元素添加到队尾
}
// 出队操作
public Integer dequeue() {
if (isEmpty()) {
throw new IllegalStateException("Queue is empty");
}
return list.removeFirst(); // 使用removeFirst()方法移除并返回队首元素
}
// 查看队首元素
public Integer peek() {
if (isEmpty()) {
throw new IllegalStateException("Queue is empty");
}
return list.getFirst(); // 使用getFirst()方法查看队首元素但不移除
}
// 检查队列是否为空
public boolean isEmpty() {
return list.isEmpty();
}
// 获取队列的大小
public int size() {
return list.size();
}
public static void main(String[] args) {
QueueWithLinkedList queue = new QueueWithLinkedList();
// 入队
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
// 查看队首元素
System.out.println("Front element: " + queue.peek()); // 输出: Front element: 1
// 出队
System.out.println("Dequeued element: " + queue.dequeue()); // 输出: Dequeued element: 1
System.out.println("New front element: " + queue.peek()); // 输出: New front element: 2
// 队列大小
System.out.println("Queue size: " + queue.size()); // 输出: Queue size: 2
}
}
2. 使用数组模拟队列
虽然Java标准库中已经提供了多种队列的实现,但了解如何使用数组手动实现队列也是很有帮助的。数组模拟队列时,需要维护两个指针:front
指向队头元素的下一个位置(或队头元素的位置,取决于具体实现),rear
指向队尾元素的下一个位置。
public class ArrayQueue {
private int[] queue; // 存储队列元素的数组
private int front; // 队首索引
private int rear; // 队尾索引
private int size; // 队列当前元素数量
private int capacity; // 队列容量
// 构造函数,初始化队列
public ArrayQueue(int capacity) {
this.capacity = capacity;
this.queue = new int[this.capacity];
this.front = 0;
this.rear = -1; // 初始时队尾索引设为-1,方便后续直接插入
this.size = 0;
}
// 判断队列是否为空
public boolean isEmpty() {
return size == 0;
}
// 判断队列是否已满
public boolean isFull() {
return size == capacity;
}
// 入队操作
public boolean enqueue(int element) {
if (isFull()) {
return false; // 队列已满,无法添加
}
rear = (rear + 1) % capacity; // 循环队列的关键,处理数组越界问题
queue[rear] = element;
size++;
return true;
}
// 出队操作
public Integer dequeue() {
if (isEmpty()) {
return null; // 队列为空,无法移除
}
int frontElement = queue[front];
front = (front + 1) % capacity; // 循环队列的关键,处理数组越界问题
size--;
return frontElement;
}
// 查看队首元素
public Integer front() {
if (isEmpty()) {
return null; // 队列为空
}
return queue[front];
}
// 队列大小
public int getSize() {
return size;
}
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(5); // 创建一个容量为5的队列
// 入队
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
// 查看队首元素
System.out.println("Front element: " + queue.front()); // 输出: Front element: 1
// 出队
System.out.println("Dequeued element: " + queue.dequeue()); // 输出: Dequeued element: 1
System.out.println("New front element: " + queue.front()); // 输出: New front element: 2
// 队列大小
System.out.println("Queue size: " + queue.getSize()); // 输出: Queue size: 2
// 继续入队,直到队列满
while (!queue.isFull()) {
queue.enqueue(queue.getSize() + 3); // 只是为了展示,实际中可能添加不同的值
}
// 此时队列已满,无法再入队
// queue.enqueue(7); // 这将不会成功,因为队列已满
}
}
四、队列的应用场景
队列在计算机科学的各个领域都有广泛的应用,包括但不限于:
- 操作系统中的进程调度:进程按照其到达的顺序排队等待处理,调度器会从队列的头部选择下一个要执行的进程。
- 网络通信中的消息队列:发送者将消息放入队列的末尾,接收者从队列的头部获取消息进行处理,以确保消息的可靠传递并减少发送者和接收者之间的直接耦合。
- 高性能计算中的任务调度:任务可能以非常快的速度同时到达,使用队列来管理任务,确保按照先到达的顺序执行任务。
- 缓存淘汰策略:如最近最少使用(LRU)策略,它维护一个队列,最近被访问过的缓存项排在队列的末尾。
- 图的搜索算法:如广度优先搜索(BFS),使用队列来维护待访问的节点。