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

目录

[1. 队列的基本概念](#1. 队列的基本概念)

[1.1 队列的定义](#1.1 队列的定义)

[1.2 队列的核心特性](#1.2 队列的核心特性)

[1.3 现实生活中的类比](#1.3 现实生活中的类比)

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

[2.1 基本操作接口](#2.1 基本操作接口)

[2.2 操作示例](#2.2 操作示例)

[3. 队列的实现方式](#3. 队列的实现方式)

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

[3.1.1 固定大小的顺序队列](#3.1.1 固定大小的顺序队列)

[3.1.2 动态扩容的顺序队列](#3.1.2 动态扩容的顺序队列)

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

[4. Java中的队列实现类](#4. Java中的队列实现类)

[4.1 Queue接口的继承体系](#4.1 Queue接口的继承体系)

[4.2 主要实现类对比](#4.2 主要实现类对比)

[4.3 具体使用示例](#4.3 具体使用示例)

LinkedList作为队列

ArrayDeque的高性能队列

[5. 队列的变种和扩展](#5. 队列的变种和扩展)

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

[5.2 优先队列(PriorityQueue)](#5.2 优先队列(PriorityQueue))

[5.3 阻塞队列(BlockingQueue)](#5.3 阻塞队列(BlockingQueue))

[5.4 循环队列(Circular Queue)](#5.4 循环队列(Circular Queue))

[6. 队列的应用场景](#6. 队列的应用场景)

[6.1 广度优先搜索(BFS)](#6.1 广度优先搜索(BFS))

[6.2 任务调度系统](#6.2 任务调度系统)

[6.3 消息队列模拟](#6.3 消息队列模拟)

[6.4 缓存淘汰策略(FIFO)](#6.4 缓存淘汰策略(FIFO))

[7. 队列相关的算法题目](#7. 队列相关的算法题目)

[7.1 用队列实现栈](#7.1 用队列实现栈)

[7.2 用栈实现队列](#7.2 用栈实现队列)

[7.3 滑动窗口最大值](#7.3 滑动窗口最大值)

[8. 队列的性能分析](#8. 队列的性能分析)

[8.1 时间复杂度分析](#8.1 时间复杂度分析)

[8.2 空间复杂度优化](#8.2 空间复杂度优化)

循环数组的优势

[9. 队列的线程安全考虑](#9. 队列的线程安全考虑)

[9.1 并发队列选择](#9.1 并发队列选择)

[9.2 线程安全示例](#9.2 线程安全示例)

[10. 队列选择指南](#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 有界阻塞,控制资源
相关推荐
岑梓铭2 小时前
《考研408数据结构》第二章《线性表(顺序表、链表)》复习笔记
数据结构·笔记·考研
我不是混子2 小时前
如何保证接口幂等性?
java·后端
_院长大人_3 小时前
阿里云云效将本地的maven相关文件批量推送到阿里云仓库以及使用
java·阿里云·maven
麦兜*3 小时前
Redis 7.0 新特性深度解读:迈向生产级的新纪元
java·数据库·spring boot·redis·spring·spring cloud·缓存
我是华为OD~HR~栗栗呀3 小时前
测试转C++开发面经(华为OD)
java·c++·后端·python·华为od·华为·面试
龙茶清欢3 小时前
最新版 springdoc-openapi-starter-webmvc-ui 常用注解详解 + 实战示例
java·spring boot·ui·spring cloud
qiu_zhongya3 小时前
iree 用C++来运行Qwen 2.5 0.5b
开发语言·c++·人工智能
汪宁宇3 小时前
giflib5.2.2 在Qt与VS C++中实现Gif缩放示例
开发语言·c++·qt