一、队列:先进先出的线性数据结构
1.1 队列的基本概念与特性
队列(Queue)是一种先进先出(First-In-First-Out, FIFO)的线性数据结构,只允许在队尾(rear)进行插入操作,在队首(front)进行删除操作。这种特性使得队列在各种需要顺序处理的场景中有着广泛的应用。

队列的核心操作:
- enqueue(入队):将元素添加到队尾
- dequeue(出队):移除并返回队首元素
- peek(查看):返回队首元素但不移除
- isEmpty:检查队列是否为空
- size:返回队列中元素数量
1.2 队列的ADT(抽象数据类型)定义
public interface Queue<T> {
void enqueue(T element); // 入队操作
T dequeue(); // 出队操作
T peek(); // 查看队首元素
boolean isEmpty(); // 判断队列是否为空
int size(); // 获取队列大小
}
二、队列的实现方式
2.1 基于数组的实现(循环队列)
public class ArrayQueue<T> implements Queue<T> {
private static final int DEFAULT_CAPACITY = 10;
private T[] elements;
private int front; // 队首指针
private int rear; // 队尾指针
private int size; // 元素数量
public ArrayQueue() {
this(DEFAULT_CAPACITY);
}
@SuppressWarnings("unchecked")
public ArrayQueue(int capacity) {
elements = (T[]) new Object[capacity];
front = 0;
rear = -1;
size = 0;
}
@Override
public void enqueue(T element) {
if (size == elements.length) {
resize(2 * elements.length); // 动态扩容
}
rear = (rear + 1) % elements.length; // 循环队列
elements[rear] = element;
size++;
}
@Override
public T dequeue() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty");
}
T element = elements[front];
elements[front] = null; // 避免内存泄漏
front = (front + 1) % elements.length; // 循环队列
size--;
// 缩容机制
if (size > 0 && size == elements.length / 4) {
resize(elements.length / 2);
}
return element;
}
@Override
public T peek() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty");
}
return elements[front];
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public int size() {
return size;
}
private void resize(int newCapacity) {
@SuppressWarnings("unchecked")
T[] newElements = (T[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
int index = (front + i) % elements.length;
newElements[i] = elements[index];
}
elements = newElements;
front = 0;
rear = size - 1;
}
}
2.2 基于链表的实现
public class LinkedQueue<T> implements Queue<T> {
private static class Node<T> {
T data;
Node<T> next;
Node(T data) {
this.data = data;
}
}
private Node<T> front;
private Node<T> rear;
private int size;
public LinkedQueue() {
front = null;
rear = null;
size = 0;
}
@Override
public void enqueue(T element) {
Node<T> newNode = new Node<>(element);
if (isEmpty()) {
front = newNode;
rear = newNode;
} else {
rear.next = newNode;
rear = newNode;
}
size++;
}
@Override
public T dequeue() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty");
}
T element = front.data;
front = front.next;
if (front == null) {
rear = null;
}
size--;
return element;
}
@Override
public T peek() {
if (isEmpty()) {
throw new NoSuchElementException("Queue is empty");
}
return front.data;
}
@Override
public boolean isEmpty() {
return front == null;
}
@Override
public int size() {
return size;
}
}
三、队列与栈的全面对比
3.1 基本特性对比
|
特性 |
队列 (Queue) |
栈 (Stack) |
|
基本原则 |
先进先出 (FIFO) |
后进先出 (LIFO) |
|
插入操作 |
在队尾 (rear) 进行 |
在栈顶 (top) 进行 |
|
删除操作 |
在队首 (front) 进行 |
在栈顶 (top) 进行 |
|
查看操作 |
查看队首元素 |
查看栈顶元素 |
|
典型应用 |
任务调度、消息传递 |
函数调用、撤销操作 |
3.2 时间复杂度对比
|
操作 |
队列 |
栈 |
说明 |
|
插入 |
O(1) |
O(1) |
两者都支持常数时间插入 |
|
删除 |
O(1) |
O(1) |
两者都支持常数时间删除 |
|
查看 |
O(1) |
O(1) |
查看队首/栈顶元素 |
|
随机访问 |
O(n) |
O(n) |
都不支持高效随机访问 |
|
搜索 |
O(n) |
O(n) |
都需要遍历查找元素 |
3.3 内存布局对比
队列的内存布局:
[元素0][元素1][元素2][元素3][元素4]... ↑ ↑ front rear 循环数组或链表实现,保持FIFO顺序
栈的内存布局:
[元素0][元素1][元素2][元素3][元素4]... ↑ top 数组或链表实现,保持LIFO顺序
3.4 优缺点总结
队列的优点:
- 保证处理顺序,先进先出
- 适合任务调度和消息传递场景
- 可以高效实现生产者-消费者模式
队列的缺点:
- 随机访问效率低
- 实现相对复杂(特别是循环队列)
- 不适合需要后进先出处理的场景
栈的优点:
- 实现简单,操作高效
- 适合递归、回溯等算法
- 内存管理效率高
栈的缺点:
- 访问受限,只能操作栈顶元素
- 不适合需要先进先出处理的场景
- 深度过大时可能栈溢出
四、队列的核心算法与应用
4.1 广度优先搜索(BFS)
public class GraphBFS {
private Map<Integer, List<Integer>> graph;
public GraphBFS() {
graph = new HashMap<>();
}
public void addEdge(int u, int v) {
graph.putIfAbsent(u, new ArrayList<>());
graph.get(u).add(v);
}
public void bfs(int start) {
Set<Integer> visited = new HashSet<>();
Queue<Integer> queue = new LinkedQueue<>();
queue.enqueue(start);
visited.add(start);
while (!queue.isEmpty()) {
int node = queue.dequeue();
System.out.print(node + " ");
if (graph.containsKey(node)) {
for (int neighbor : graph.get(node)) {
if (!visited.contains(neighbor)) {
visited.add(neighbor);
queue.enqueue(neighbor);
}
}
}
}
}
}
// 使用示例
GraphBFS graph = new GraphBFS();
graph.addEdge(0, 1);
graph.addEdge(0, 2);
graph.addEdge(1, 3);
graph.addEdge(2, 4);
graph.bfs(0); // 输出: 0 1 2 3 4
4.2 二叉树层次遍历
public class BinaryTreeLevelOrder {
static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedQueue<>();
queue.enqueue(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.dequeue();
currentLevel.add(node.val);
if (node.left != null) {
queue.enqueue(node.left);
}
if (node.right != null) {
queue.enqueue(node.right);
}
}
result.add(currentLevel);
}
return result;
}
}
五、队列的变种与高级实现
5.1 双端队列(Deque)
public interface Deque<T> {
void addFirst(T element);
void addLast(T element);
T removeFirst();
T removeLast();
T getFirst();
T getLast();
boolean isEmpty();
int size();
}
// 使用Java内置Deque
Deque<Integer> deque = new LinkedList<>();
deque.addFirst(1); // 前端添加
deque.addLast(2); // 后端添加
int first = deque.removeFirst(); // 前端移除
int last = deque.removeLast(); // 后端移除
5.2 优先级队列(Priority Queue)
public class TaskScheduler {
static class Task implements Comparable<Task> {
String name;
int priority;
LocalDateTime scheduledTime;
Task(String name, int priority, LocalDateTime scheduledTime) {
this.name = name;
this.priority = priority;
this.scheduledTime = scheduledTime;
}
@Override
public int compareTo(Task other) {
// 先按优先级,再按时间排序
if (this.priority != other.priority) {
return Integer.compare(other.priority, this.priority); // 优先级高的在前
}
return this.scheduledTime.compareTo(other.scheduledTime);
}
}
private PriorityQueue<Task> taskQueue;
public TaskScheduler() {
taskQueue = new PriorityQueue<>();
}
public void scheduleTask(Task task) {
taskQueue.offer(task);
}
public Task getNextTask() {
return taskQueue.poll();
}
}
5.3 阻塞队列(Blocking Queue)
public class ProducerConsumerExample {
private BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);
class Producer implements Runnable {
public void run() {
try {
for (int i = 0; i < 100; i++) {
queue.put(i); // 队列满时阻塞
System.out.println("Produced: " + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
class Consumer implements Runnable {
public void run() {
try {
while (true) {
Integer value = queue.take(); // 队列空时阻塞
System.out.println("Consumed: " + value);
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
六、队列与栈的实际应用场景
6.1 队列的典型应用场景
场景1:消息队列系统
public class MessageQueue {
private Queue<String> queue;
private final Object lock = new Object();
public MessageQueue() {
queue = new LinkedQueue<>();
}
public void produce(String message) throws InterruptedException {
synchronized (lock) {
queue.enqueue(message);
lock.notifyAll(); // 通知消费者
}
}
public String consume() throws InterruptedException {
synchronized (lock) {
while (queue.isEmpty()) {
lock.wait(); // 等待消息
}
return queue.dequeue();
}
}
}
场景2:线程池任务调度
public class SimpleThreadPool {
private BlockingQueue<Runnable> taskQueue;
private List<WorkerThread> workers;
public SimpleThreadPool(int poolSize) {
taskQueue = new LinkedBlockingQueue<>();
workers = new ArrayList<>();
for (int i = 0; i < poolSize; i++) {
WorkerThread worker = new WorkerThread(taskQueue);
workers.add(worker);
worker.start();
}
}
public void execute(Runnable task) {
taskQueue.offer(task);
}
class WorkerThread extends Thread {
private BlockingQueue<Runnable> queue;
WorkerThread(BlockingQueue<Runnable> queue) {
this.queue = queue;
}
public void run() {
while (!isInterrupted()) {
try {
Runnable task = queue.take();
task.run();
} catch (InterruptedException e) {
interrupt();
}
}
}
}
}
6.2 栈的典型应用场景
场景1:函数调用栈
public class FunctionCallExample {
public void functionA() {
System.out.println("Entering functionA");
functionB();
System.out.println("Exiting functionA");
}
public void functionB() {
System.out.println("Entering functionB");
functionC();
System.out.println("Exiting functionB");
}
public void functionC() {
System.out.println("Entering functionC");
// 执行操作
System.out.println("Exiting functionC");
}
}
场景2:浏览器前进后退功能
public class BrowserHistory {
private Stack<String> backStack;
private Stack<String> forwardStack;
private String currentPage;
public BrowserHistory(String homepage) {
backStack = new ArrayStack<>();
forwardStack = new ArrayStack<>();
currentPage = homepage;
}
public void visit(String url) {
backStack.push(currentPage);
currentPage = url;
forwardStack.clear();
}
public String back() {
if (!backStack.isEmpty()) {
forwardStack.push(currentPage);
currentPage = backStack.pop();
}
return currentPage;
}
public String forward() {
if (!forwardStack.isEmpty()) {
backStack.push(currentPage);
currentPage = forwardStack.pop();
}
return currentPage;
}
}
6.3 队列与栈结合使用的场景
场景:用队列实现栈,用栈实现队列
// 用队列实现栈
class StackUsingQueues<T> {
private Queue<T> queue1;
private Queue<T> queue2;
public StackUsingQueues() {
queue1 = new LinkedQueue<>();
queue2 = new LinkedQueue<>();
}
public void push(T element) {
queue2.enqueue(element);
while (!queue1.isEmpty()) {
queue2.enqueue(queue1.dequeue());
}
// 交换queue1和queue2
Queue<T> temp = queue1;
queue1 = queue2;
queue2 = temp;
}
public T pop() {
if (queue1.isEmpty()) {
throw new NoSuchElementException("Stack is empty");
}
return queue1.dequeue();
}
}
// 用栈实现队列
class QueueUsingStacks<T> {
private Stack<T> stack1;
private Stack<T> stack2;
public QueueUsingStacks() {
stack1 = new ArrayStack<>();
stack2 = new ArrayStack<>();
}
public void enqueue(T element) {
stack1.push(element);
}
public T dequeue() {
if (stack2.isEmpty()) {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
if (stack2.isEmpty()) {
throw new NoSuchElementException("Queue is empty");
}
return stack2.pop();
}
}
七、总结与选择指南
7.1 如何选择队列还是栈?
选择队列的情况:
- 需要保证先进先出的处理顺序
- 实现任务调度、消息传递系统
- 广度优先搜索(BFS)算法
- 生产者-消费者模式
- 缓存系统(如LRU缓存)
选择栈的情况:
- 需要后进先出的处理顺序
- 函数调用、递归实现
- 深度优先搜索(DFS)算法
- 括号匹配、表达式求值
- 撤销/重做功能
7.2 性能优化建议
- 队列优化:
- 使用循环队列避免数据迁移
- 根据场景选择合适的队列实现(普通队列、双端队列、优先级队列)
- 多线程环境使用线程安全队列
- 栈优化:
- 使用数组实现提高缓存性能
- 避免过深的递归调用,使用迭代+显式栈
- 注意栈溢出问题,合理设置栈大小
7.3 Java集合框架中的实现
// Java内置队列实现 Queue<Integer> queue1 = new LinkedList<>(); // 普通队列 Deque<Integer> deque = new ArrayDeque<>(); // 双端队列 Queue<Integer> priorityQueue = new PriorityQueue<>(); // 优先级队列 // Java内置栈实现 Stack<Integer> stack1 = new Stack<>(); // 传统栈 Deque<Integer> stack2 = new ArrayDeque<>(); // 推荐使用的栈(更高效)
通过本文的学习,你应该已经掌握了队列的原理、实现方式以及与栈的对比选择。在实际开发中,根据具体需求选择合适的数据结构,是编写高效Java程序的关键技能之一。队列和栈作为基础数据结构,在算法设计、系统开发中都有着不可替代的作用。

被折叠的 条评论
为什么被折叠?



