《数据之美》:队列的世界与算法实践

简介: 队列是先进先出的线性数据结构,常用于任务调度、消息传递等场景。本文详解其数组与链表实现、与栈的对比、核心算法(如BFS)、变种(双端队列、优先级队列)及实际应用,助你掌握选择与优化策略。

一、队列:先进先出的线性数据结构

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 优缺点总结

队列的优点

  1. 保证处理顺序,先进先出
  2. 适合任务调度和消息传递场景
  3. 可以高效实现生产者-消费者模式

队列的缺点

  1. 随机访问效率低
  2. 实现相对复杂(特别是循环队列)
  3. 不适合需要后进先出处理的场景

栈的优点

  1. 实现简单,操作高效
  2. 适合递归、回溯等算法
  3. 内存管理效率高

栈的缺点

  1. 访问受限,只能操作栈顶元素
  2. 不适合需要先进先出处理的场景
  3. 深度过大时可能栈溢出

四、队列的核心算法与应用

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 如何选择队列还是栈?

选择队列的情况

  1. 需要保证先进先出的处理顺序
  2. 实现任务调度、消息传递系统
  3. 广度优先搜索(BFS)算法
  4. 生产者-消费者模式
  5. 缓存系统(如LRU缓存)

选择栈的情况

  1. 需要后进先出的处理顺序
  2. 函数调用、递归实现
  3. 深度优先搜索(DFS)算法
  4. 括号匹配、表达式求值
  5. 撤销/重做功能

7.2 性能优化建议

  1. 队列优化
  2. 使用循环队列避免数据迁移
  3. 根据场景选择合适的队列实现(普通队列、双端队列、优先级队列)
  4. 多线程环境使用线程安全队列
  5. 栈优化
  6. 使用数组实现提高缓存性能
  7. 避免过深的递归调用,使用迭代+显式栈
  8. 注意栈溢出问题,合理设置栈大小

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程序的关键技能之一。队列和栈作为基础数据结构,在算法设计、系统开发中都有着不可替代的作用。

相关文章
|
5天前
|
存储 弹性计算 缓存
阿里云服务器租赁费用:新版租赁收费标准及活动报价参考
本文更新了2026年阿里云全系列云服务器租赁活动报价,所有特惠资源均可前往阿里云活动中心选购,整体覆盖从个人入门到企业级高性能场景的全梯度需求。其中轻量应用服务器主打极致性价比,2核2G峰值200M带宽配置每日10点、15点限时抢购价仅38元/年,2核4G配置379元/年起;高性价比的经济型e实例、通用算力型u2i实例覆盖2核4G至4核32G全档位,适配开发测试与中小型企业业务;搭载英特尔至强6处理器的第九代c9i企业级实例算力较上代提升20%,支撑高并发生产环境,不同实例规格价差清晰,用户可根据自身业务负载与预算灵活选型。
1575 112
|
12天前
|
云安全 人工智能 运维
阿里云联动百位企业安全专家,共识Agent防御最佳实践
当Agent成为新员工,你的安全边界在哪里?
1942 8
阿里云联动百位企业安全专家,共识Agent防御最佳实践
|
6天前
|
人工智能 程序员 API
Codex 接入 DeepSeek-V4-Flash:还能补上识图,提供两套方案
Codex 接入 DeepSeek-V4-Flash 怎么配?本文覆盖 CLI 与桌面端,再用 qwen3-vl-flash 补识图,两套方案可直接照做
1000 3
|
6天前
|
编解码 人工智能 安全
2核4G/4核8G/8核16G阿里云服务器如何选择实例?经济型e、通用算力型u2i与计算型c9i选哪个?
本文介绍了阿里云2核4G、4核8G、8核16G三档主流配置下经济型e、通用算力型u2i和计算型c9i三种实例的最新活动价格与适用场景。同配置下三者价差显著,以2核4G为例,经济型e低至599.93元/年,计算型c9i则高达1742.08元/年。文章详细解析了各实例的性能定位:经济型e适合轻负载入门场景,u2i兼顾稳定算力与性价比,c9i凭借第9代至强处理器与芯片级安全能力支撑高性能业务。同时提示用户可叠加满减优惠券享受折上折,建议根据业务负载与预算综合决策。
529 112
|
18天前
|
人工智能 前端开发 Linux
Codex 桌面版安装 + CC Switch 接入第三方 API 完整教程(2026 最新)
2026最新教程:手把手教你安装Codex桌面版,通过CC Switch v3.17.0一键接入Fenno等国产API(兼容OpenAI Responses格式),跳过账号登录,完整启用代码审查、多步任务与上下文感知功能。零基础友好,全程图文实操。(239字)
2567 4
|
10天前
|
存储 人工智能 关系型数据库
阿里云AI产品与云产品最新组合套餐:Token Plan、AI coding及云服务器和建站等组合优惠价
阿里云推出全新“算力+模型+应用”一站式云与AI组合套餐活动,覆盖从个人开发者到中大型企业的全场景需求。核心亮点为分三档定价的Token Plan订阅服务,支持Qwen3.8-Max-Preview大模型调用,错峰时段最低可享0.2折优惠。活动同步推出AI Coding、智能体部署、云电脑托管、0代码建站等十余类场景化组合,搭配99元/年的普惠云服务器、88元/年的入门数据库等经典特惠产品,还为企业提供1V1定制化AI转型方案,大幅降低了不同用户群体拥抱AI的技术门槛与采购成本。
723 111
|
20天前
|
人工智能 JSON 安全
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
阿里云AI安全产品联动防御Fastjson攻击
2638 13
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
|
6天前
|
人工智能 JSON Shell
2026AI漫剧本地全开源方案(附各个软件模型链接),8G显卡也能流畅运行
这是一套完全本地化部署的AI漫剧生成技术链路:涵盖LLM剧本分镜生成、FLUX文生图(IP-Adapter人脸锁定)、StoryDiffusion时序连贯控制、LTX-2.3唇形同步视频生成,及ComfyUI全流程调度。零云端费用,仅耗硬件算力,单集2–4小时可产出竖屏短视频,适配抖音/B站分发。
|
7天前
Qoder 一周年 × Qwen3.8-Max 正式上线,多重好礼限时领
8月3日,Qwen3.8-Max 正式上线Qoder,迎来Qoder一周年。新老用户可领800次免费调用,下单再赠2000次;夜间(22:00–08:00)调用5折;邀请好友双方得积分与调用额度。
450 1

热门文章

最新文章