当前位置: 首页 > news >正文

【数据结构】队列(Queue)全面详解

目录

1. 队列的基本概念

1.1 队列的定义

1.2 队列的核心特性

1.3 现实生活中的类比

2. 队列的抽象数据类型(ADT)

2.1 基本操作接口

2.2 操作示例

3. 队列的实现方式

3.1 基于数组的实现(顺序队列)

3.1.1 固定大小的顺序队列

3.1.2 动态扩容的顺序队列

3.2 基于链表的实现(链式队列)

4. Java中的队列实现类

4.1 Queue接口的继承体系

4.2 主要实现类对比

4.3 具体使用示例

LinkedList作为队列

ArrayDeque的高性能队列

5. 队列的变种和扩展

5.1 双端队列(Deque - Double Ended Queue)

5.2 优先队列(PriorityQueue)

5.3 阻塞队列(BlockingQueue)

5.4 循环队列(Circular Queue)

6. 队列的应用场景

6.1 广度优先搜索(BFS)

6.2 任务调度系统

6.3 消息队列模拟

6.4 缓存淘汰策略(FIFO)

7. 队列相关的算法题目

7.1 用队列实现栈

7.2 用栈实现队列

7.3 滑动窗口最大值

8. 队列的性能分析

8.1 时间复杂度分析

8.2 空间复杂度优化

循环数组的优势

9. 队列的线程安全考虑

9.1 并发队列选择

9.2 线程安全示例

10. 队列选择指南


1. 队列的基本概念

1.1 队列的定义

队列是一种​​先进先出(FIFO, First-In-First-Out)​​ 的线性数据结构。它只允许在表的一端进行插入操作,在另一端进行删除操作。

1.2 队列的核心特性

  • ​先进先出原则​​:最先进入队列的元素将最先被移除

  • ​两端操作​​:只能在一端(队尾)添加元素,在另一端(队首)移除元素

  • ​有序性​​:元素按照进入的顺序排队

1.3 现实生活中的类比

排队买票场景:队尾(入队) → [顾客A][顾客B][顾客C][顾客D] → 队首(出队)后到的人排在队尾          排队中         先到的人在队首

2. 队列的抽象数据类型(ADT)

2.1 基本操作接口

操作

方法签名

描述

异常处理

返回特殊值

​入队​

boolean add(E e)

添加元素到队尾

失败抛异常

boolean offer(E e)

​出队​

E remove()

移除并返回队首元素

失败抛异常

E poll()

​查看队首​

E element()

返回队首元素(不移除)

失败抛异常

E peek()

​判空​

boolean isEmpty()

判断队列是否为空

-

-

​大小​

int size()

返回队列元素个数

-

-

2.2 操作示例

Queue<String> queue = new LinkedList<>();// 入队操作
queue.offer("A");
queue.offer("B");
queue.offer("C");
// 队列状态: [A, B, C] ← 队尾// 查看队首
System.out.println("队首元素: " + queue.peek()); // A// 出队操作
while (!queue.isEmpty()) {String element = queue.poll();System.out.println("出队: " + element);
}
// 输出: A, B, C (先进先出)

3. 队列的实现方式

3.1 基于数组的实现(顺序队列)

3.1.1 固定大小的顺序队列
public class ArrayQueue<E> {private E[] array;private int front; // 队首指针private int rear;  // 队尾指针private int size;  // 元素个数private final int capacity;@SuppressWarnings("unchecked")public ArrayQueue(int capacity) {this.capacity = capacity;this.array = (E[]) new Object[capacity];this.front = 0;this.rear = -1;this.size = 0;}public boolean offer(E element) {if (isFull()) return false;rear = (rear + 1) % capacity; // 循环利用数组空间array[rear] = element;size++;return true;}public E poll() {if (isEmpty()) return null;E element = array[front];array[front] = null; // 帮助垃圾回收front = (front + 1) % capacity;size--;return element;}public E peek() {if (isEmpty()) return null;return array[front];}public boolean isEmpty() { return size == 0; }public boolean isFull() { return size == capacity; }public int size() { return size; }
}
3.1.2 动态扩容的顺序队列
public class DynamicArrayQueue<E> {private E[] array;private int front;private int rear;private int size;private static final int DEFAULT_CAPACITY = 10;@SuppressWarnings("unchecked")public DynamicArrayQueue() {this.array = (E[]) new Object[DEFAULT_CAPACITY];this.front = 0;this.rear = -1;this.size = 0;}public boolean offer(E element) {if (isFull()) {resize(); // 自动扩容}rear = (rear + 1) % array.length;array[rear] = element;size++;return true;}@SuppressWarnings("unchecked")private void resize() {int newCapacity = array.length * 2;E[] newArray = (E[]) new Object[newCapacity];// 将元素按顺序复制到新数组for (int i = 0; i < size; i++) {newArray[i] = array[(front + i) % array.length];}array = newArray;front = 0;rear = size - 1;}
}

3.2 基于链表的实现(链式队列)

public class LinkedQueue<E> {// 节点定义private static class Node<E> {E data;Node<E> next;Node(E data) {this.data = data;}}private Node<E> front; // 队首节点private Node<E> rear;  // 队尾节点private int size;public LinkedQueue() {front = rear = null;size = 0;}public boolean offer(E element) {Node<E> newNode = new Node<>(element);if (isEmpty()) {front = rear = newNode;} else {rear.next = newNode;rear = newNode;}size++;return true;}public E poll() {if (isEmpty()) return null;E element = front.data;front = front.next;if (front == null) {rear = null; // 队列为空时,rear也要置空}size--;return element;}public E peek() {if (isEmpty()) return null;return front.data;}public boolean isEmpty() { return front == null; }public int size() { return size; }
}

4. Java中的队列实现类

4.1 Queue接口的继承体系

Collection ← Queue ← Deque ← LinkedList/ArrayDeque

4.2 主要实现类对比

实现类

底层结构

线程安全

特性

适用场景

​LinkedList​

双向链表

可作队列/双端队列

一般队列需求

​ArrayDeque​

循环数组

高性能,容量可调

​推荐的标准队列​

​PriorityQueue​

堆(数组)

元素按优先级出队

优先队列

​ConcurrentLinkedQueue​

链表

高并发非阻塞

高并发场景

​ArrayBlockingQueue​

数组

有界阻塞队列

生产者-消费者

​LinkedBlockingQueue​

链表

可选有界阻塞队列

生产者-消费者

4.3 具体使用示例

LinkedList作为队列
Queue<String> queue = new LinkedList<>();// 基本操作
queue.offer("A");
queue.offer("B");
queue.offer("C");System.out.println("队首元素: " + queue.peek()); // A
System.out.println("出队: " + queue.poll());    // A
System.out.println("队列大小: " + queue.size()); // 2
ArrayDeque的高性能队列
Queue<Integer> queue = new ArrayDeque<>(100); // 预分配容量// 批量操作性能更好
for (int i = 0; i < 1000; i++) {queue.offer(i);
}while (!queue.isEmpty()) {Integer num = queue.poll();// 处理任务
}

5. 队列的变种和扩展

5.1 双端队列(Deque - Double Ended Queue)

Deque<String> deque = new ArrayDeque<>();// 作为栈使用(后进先出)
deque.push("A"); // 添加到队首
deque.push("B");
deque.push("C");
System.out.println("弹出: " + deque.pop()); // C// 作为队列使用(先进先出)
deque.offerLast("X"); // 添加到队尾
deque.offerLast("Y");
System.out.println("出队: " + deque.pollFirst()); // X// 双端操作
deque.offerFirst("Z"); // 添加到队首
deque.offerLast("W");  // 添加到队尾

5.2 优先队列(PriorityQueue)

// 自然顺序(最小堆)
Queue<Integer> minHeap = new PriorityQueue<>();
minHeap.offer(5);
minHeap.offer(1);
minHeap.offer(3);
minHeap.offer(2);
minHeap.offer(4);System.out.println("出队顺序:");
while (!minHeap.isEmpty()) {System.out.print(minHeap.poll() + " "); // 1 2 3 4 5
}// 自定义优先级
Queue<String> lengthQueue = new PriorityQueue<>((a, b) -> a.length() - b.length() // 按字符串长度排序
);lengthQueue.offer("apple");
lengthQueue.offer("banana");
lengthQueue.offer("cat");while (!lengthQueue.isEmpty()) {System.out.println(lengthQueue.poll()); // cat, apple, banana
}

5.3 阻塞队列(BlockingQueue)

BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);// 生产者线程
new Thread(() -> {try {for (int i = 0; i < 100; i++) {queue.put(i); // 如果队列满,会阻塞等待System.out.println("生产: " + i);Thread.sleep(100);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}
}).start();// 消费者线程
new Thread(() -> {try {while (true) {Integer item = queue.take(); // 如果队列空,会阻塞等待System.out.println("消费: " + item);Thread.sleep(150);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}
}).start();

5.4 循环队列(Circular Queue)

public class CircularQueue {private int[] data;private int front;private int rear;private int size;private final int capacity;public CircularQueue(int k) {capacity = k;data = new int[capacity];front = 0;rear = -1;size = 0;}public boolean enQueue(int value) {if (isFull()) return false;rear = (rear + 1) % capacity;data[rear] = value;size++;return true;}public boolean deQueue() {if (isEmpty()) return false;front = (front + 1) % capacity;size--;return true;}public int Front() {return isEmpty() ? -1 : data[front];}public int Rear() {return isEmpty() ? -1 : data[rear];}public boolean isEmpty() { return size == 0; }public boolean isFull() { return size == capacity; }
}

6. 队列的应用场景

6.1 广度优先搜索(BFS)

// 二叉树的层次遍历
public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> result = new ArrayList<>();if (root == null) return result;Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {int levelSize = queue.size();List<Integer> level = new ArrayList<>();for (int i = 0; i < levelSize; i++) {TreeNode node = queue.poll();level.add(node.val);if (node.left != null) queue.offer(node.left);if (node.right != null) queue.offer(node.right);}result.add(level);}return result;
}// 图的BFS
public void bfs(Graph graph, int start) {boolean[] visited = new boolean[graph.size()];Queue<Integer> queue = new LinkedList<>();visited[start] = true;queue.offer(start);while (!queue.isEmpty()) {int node = queue.poll();System.out.println("访问节点: " + node);for (int neighbor : graph.getNeighbors(node)) {if (!visited[neighbor]) {visited[neighbor] = true;queue.offer(neighbor);}}}
}

6.2 任务调度系统

public class TaskScheduler {private final Queue<Runnable> taskQueue = new LinkedList<>();private final ExecutorService executor = Executors.newFixedThreadPool(4);private volatile boolean isRunning = true;public void submitTask(Runnable task) {synchronized (taskQueue) {taskQueue.offer(task);taskQueue.notifyAll(); // 唤醒等待的消费者线程}}public void start() {for (int i = 0; i < 4; i++) {executor.execute(this::processTasks);}}private void processTasks() {while (isRunning) {Runnable task;synchronized (taskQueue) {while (taskQueue.isEmpty() && isRunning) {try {taskQueue.wait(); // 等待新任务} catch (InterruptedException e) {Thread.currentThread().interrupt();return;}}if (!isRunning) return;task = taskQueue.poll();}// 执行任务try {task.run();} catch (Exception e) {System.err.println("任务执行异常: " + e.getMessage());}}}public void shutdown() {isRunning = false;synchronized (taskQueue) {taskQueue.notifyAll();}executor.shutdown();}
}

6.3 消息队列模拟

public class MessageQueue {private final Queue<String> queue = new LinkedList<>();private final int maxSize;private final Object lock = new Object();public MessageQueue(int maxSize) {this.maxSize = maxSize;}// 生产者public void produce(String message) throws InterruptedException {synchronized (lock) {while (queue.size() == maxSize) {System.out.println("队列已满,生产者等待...");lock.wait();}queue.offer(message);System.out.println("生产消息: " + message + ",当前队列大小: " + queue.size());lock.notifyAll(); // 唤醒消费者}}// 消费者public String consume() throws InterruptedException {synchronized (lock) {while (queue.isEmpty()) {System.out.println("队列为空,消费者等待...");lock.wait();}String message = queue.poll();System.out.println("消费消息: " + message + ",当前队列大小: " + queue.size());lock.notifyAll(); // 唤醒生产者return message;}}
}

6.4 缓存淘汰策略(FIFO)

public class FIFOCache<K, V> {private final int capacity;private final Queue<K> keyQueue;private final Map<K, V> cache;public FIFOCache(int capacity) {this.capacity = capacity;this.keyQueue = new LinkedList<>();this.cache = new HashMap<>();}public V get(K key) {return cache.get(key);}public void put(K key, V value) {if (cache.containsKey(key)) {// 更新现有键的值cache.put(key, value);return;}if (keyQueue.size() == capacity) {// 淘汰最早的元素K oldestKey = keyQueue.poll();cache.remove(oldestKey);System.out.println("淘汰键: " + oldestKey);}// 添加新元素keyQueue.offer(key);cache.put(key, value);System.out.println("添加键: " + key);}public void display() {System.out.println("当前缓存内容:");for (K key : keyQueue) {System.out.println(key + " -> " + cache.get(key));}}
}

7. 队列相关的算法题目

7.1 用队列实现栈

class MyStack {private Queue<Integer> queue;public MyStack() {queue = new LinkedList<>();}public void push(int x) {queue.offer(x);// 将前面的元素重新入队,让新元素在队首int size = queue.size();for (int i = 0; i < size - 1; i++) {queue.offer(queue.poll());}}public int pop() {return queue.poll();}public int top() {return queue.peek();}public boolean empty() {return queue.isEmpty();}
}

7.2 用栈实现队列

class MyQueue {private Stack<Integer> inStack;private Stack<Integer> outStack;public MyQueue() {inStack = new Stack<>();outStack = new Stack<>();}public void push(int x) {inStack.push(x);}public int pop() {if (outStack.isEmpty()) {while (!inStack.isEmpty()) {outStack.push(inStack.pop());}}return outStack.pop();}public int peek() {if (outStack.isEmpty()) {while (!inStack.isEmpty()) {outStack.push(inStack.pop());}}return outStack.peek();}public boolean empty() {return inStack.isEmpty() && outStack.isEmpty();}
}

7.3 滑动窗口最大值

public int[] maxSlidingWindow(int[] nums, int k) {if (nums == null || nums.length == 0) return new int[0];int n = nums.length;int[] result = new int[n - k + 1];Deque<Integer> deque = new ArrayDeque<>(); // 存储索引for (int i = 0; i < n; i++) {// 移除超出窗口范围的元素while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {deque.pollFirst();}// 移除比当前元素小的元素,保持递减顺序while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {deque.pollLast();}deque.offerLast(i);// 当窗口形成时,记录最大值if (i >= k - 1) {result[i - k + 1] = nums[deque.peekFirst()];}}return result;
}

8. 队列的性能分析

8.1 时间复杂度分析

操作

基于数组

基于链表

说明

​入队 (enqueue)​

O(1) 平均

O(1)

尾部添加

​出队 (dequeue)​

O(1)

O(1)

头部移除

​查看队首 (peek)​

O(1)

O(1)

访问头部

​查找 (contains)​

O(n)

O(n)

需要遍历

​空间复杂度​

O(n)

O(n)

存储n个元素

​注意​​:PriorityQueue的入队出队时间复杂度为 O(log n)

8.2 空间复杂度优化

循环数组的优势
// 循环数组避免数据搬移,提高性能
public class CircularArrayQueue {private int[] data;private int head; // 队头索引private int tail; // 队尾索引private int size; // 元素个数public boolean enqueue(int item) {if (size == data.length) return false;data[tail] = item;tail = (tail + 1) % data.length; // 循环利用size++;return true;}
}

9. 队列的线程安全考虑

9.1 并发队列选择

场景

推荐实现

特点

​高并发读多写少​

ConcurrentLinkedQueue

非阻塞,高性能

​生产者-消费者​

ArrayBlockingQueue

有界,阻塞操作

​高吞吐量​

LinkedBlockingQueue

可选有界,吞吐量高

​延迟任务​

DelayQueue

元素按延迟时间排序

​优先级任务​

PriorityBlockingQueue

线程安全的优先队列

9.2 线程安全示例

// 使用ConcurrentLinkedQueue
Queue<String> concurrentQueue = new ConcurrentLinkedQueue<>();// 多线程安全操作
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {final int taskId = i;executor.submit(() -> {concurrentQueue.offer("Task-" + taskId);String task = concurrentQueue.poll();// 处理任务...});
}

10. 队列选择指南

需求场景

推荐实现

理由

​一般队列需求​

ArrayDeque

性能最好,内存连续

​需要双端操作​

ArrayDeque

支持栈和队列操作

​需要优先级​

PriorityQueue

按优先级出队

​高并发场景​

ConcurrentLinkedQueue

非阻塞线程安全

​生产者-消费者​

ArrayBlockingQueue

有界阻塞,控制资源

http://www.dtcms.com/a/405992.html

相关文章:

  • 网站做短信接口具体方法哪个网站做ppt
  • Android compose屏幕适配终极解决方案
  • 无人机飞行高度模块技术解析
  • 会议安排问题之贪心算法
  • H3C smart-link实验
  • IMX6ULL--EPIT,GPT
  • 前端经验:完美的圆角
  • Vue3组件通信的方法有哪些?
  • 学习嵌入式的第四十一天——ARM——时钟与定时器
  • 淮安网站建设优化北京h5网站建设报价
  • Qt 网络编程
  • ORBSLAM3-优化函数整理
  • 计算机视觉:安防智能体的实现与应用基于YOLOv8的实时无人机检测与跟踪
  • 【apifox】安装要点
  • 网站图片一般的像素企业网站需要多大空间
  • 做网站需要给设计提供专业的商城网站开发
  • 《Spring MVC奇幻漂流记:当Java遇上Web的奇妙冒险》
  • 前端性能优化,给录音播放的列表加个播放按键,点击之后再播放录音。减少页面的渲染录音文件数量过多导致加载缓慢
  • uniapp中封装底部跳转方法
  • Kafka-保证消息消费的顺序性及高可用机制
  • 通过kafka-connect 实现debezium数据监听采集
  • GTSAM 中自定义因子(Custom Factor)的详解和实战示例
  • 要建一个网站怎么做老板合作网站开发
  • 【Linux基础知识系列:第一百三十九篇】使用Bash编写函数提升脚本功能
  • 【MyBatis-Plus 动态数据源的默认行为】
  • GaussDB 和 openGauss 怎么区分?
  • 云服务器里的IP是什么意思,他们之间有什么关系?
  • @Transactional 事务注解
  • PaddleLabel百度飞桨Al Studio图像标注平台安装和使用指南(包冲突 using the ‘flask‘ extra、眼底医疗分割数据集演示)
  • 锦州网站建设工作如何快速网络推广