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

数据结构 —— 队列

        队列是一种重要的线性数据结构,遵循 “先进先出”(FIFO,First In First Out)的原则,即最早进入队列的元素最先被取出。队列广泛应用于调度系统、缓冲处理、广度优先搜索等场景。在算法中,巧妙地利用队列这种数据结构可以很有效地解决问题,在实际的工程应用中,有很多的技术也用到了队列这种数据结构。

一、队列的基本概念

  • 队头(Front):队列中第一个元素的位置,是元素出队的一端。
  • 队尾(Rear):队列中最后一个元素的位置,是元素入队的一端。
  • 基本操作:
  • 入队(Enqueue):在队尾添加元素。
  • 出队(Dequeue):从队头移除元素。
  • 查看队头(Peek/Front):返回队头元素(不删除)。
  • 判空(IsEmpty):检查队列是否为空。
  • 求长度(Size):返回队列中元素的个数。

二、队列的实现方式

队列的实现通常基于数组或链表,两种方式各有优劣:

1. 基于数组的队列(顺序队列)

  • 原理:用固定大小的数组存储元素,通过两个指针(frontrear)分别指向队头和队尾。
  • 问题:随着元素的入队和出队,frontrear 会逐渐后移,最终导致数组 “假溢出”(即数组末尾有空间,但因 rear 已达边界无法入队)。
  • 优化:采用循环队列(环形队列),将数组首尾相连,当 rear 到达数组末尾时,若前方有空位则绕回头部,解决假溢出问题。
  • 循环队列的关键操作:                 
  • 入队:rear = (rear + 1) % 容量 
  • 出队:front = (front + 1) % 容量   
  • 判空:front == rear
  • 判满:(rear + 1) % 容量 == front(这里牺牲一个位置区分空和满)

代码实现(循环队列):

public class CircularQueue {private int[] queue;    // 存储元素的数组private int front;      // 队头指针(指向第一个元素)private int rear;       // 队尾指针(指向最后一个元素的下一位)private int capacity;   // 队列容量private int size;       // 当前元素数量// 初始化队列public CircularQueue(int capacity) {this.capacity = capacity;queue = new int[capacity];front = 0;rear = 0;size = 0;}// 判断队列是否为空public boolean isEmpty() {return size == 0;}// 判断队列是否已满public boolean isFull() {return size == capacity;}// 入队操作(队尾添加元素)public void enqueue(int item) {if (isFull()) {throw new RuntimeException("队列已满,无法入队");}queue[rear] = item;rear = (rear + 1) % capacity;  // 队尾指针循环后移size++;}// 出队操作(队头移除元素)public int dequeue() {if (isEmpty()) {throw new RuntimeException("队列为空,无法出队");}int item = queue[front];front = (front + 1) % capacity;  // 队头指针循环后移size--;return item;}// 查看队头元素(不删除)public int peek() {if (isEmpty()) {throw new RuntimeException("队列为空,无元素");}return queue[front];}// 获取队列当前元素数量public int size() {return size;}// 测试示例public static void main(String[] args) {CircularQueue queue = new CircularQueue(3);queue.enqueue(1);queue.enqueue(2);queue.enqueue(3);System.out.println(queue.dequeue());  // 输出:1queue.enqueue(4);System.out.println(queue.peek());     // 输出:2System.out.println(queue.size());     // 输出:3}
}

2. 基于链表的队列(链式队列)

  • 原理:用链表存储元素,队头指向链表头节点,队尾指向链表尾节点。
  • 优势:无需预先指定容量,动态扩容,避免溢出问题,实现简单。
  • 劣势:相比数组,链表的指针操作会带来额外的内存开销。

代码实现(基于链表):

// 链表节点类
class Node {int data;       // 节点数据Node next;      // 指向下一个节点的指针public Node(int data) {this.data = data;this.next = null;}
}// 链式队列实现
public class LinkedQueue {private Node front;  // 队头指针(指向第一个节点)private Node rear;   // 队尾指针(指向最后一个节点)private int size;    // 当前元素数量// 初始化队列public LinkedQueue() {front = null;rear = null;size = 0;}// 判断队列是否为空public boolean isEmpty() {return size == 0;}// 入队操作(队尾添加元素)public void enqueue(int item) {Node newNode = new Node(item);if (isEmpty()) {// 空队列时,队头和队尾指向同一个节点front = newNode;rear = newNode;} else {// 非空队列时,新节点接在队尾,更新队尾指针rear.next = newNode;rear = newNode;}size++;}// 出队操作(队头移除元素)public int dequeue() {if (isEmpty()) {throw new RuntimeException("队列为空,无法出队");}int item = front.data;front = front.next;  // 队头指针后移size--;// 若队列清空,需将队尾指针也置空if (isEmpty()) {rear = null;}return item;}// 查看队头元素(不删除)public int peek() {if (isEmpty()) {throw new RuntimeException("队列为空,无元素");}return front.data;}// 获取队列当前元素数量public int size() {return size;}// 测试示例public static void main(String[] args) {LinkedQueue queue = new LinkedQueue();queue.enqueue(10);queue.enqueue(20);queue.enqueue(30);System.out.println(queue.dequeue());  // 输出:10System.out.println(queue.peek());     // 输出:20System.out.println(queue.size());     // 输出:2}
}

三、Java中队列的实现

        由于作者是一位Java后端开发人员,所以这里主要以Java中队列的实现形式来详细介绍队列的扩展功能。

在Java 中主要通过java.util.Queue接口规范了队列的基本操作,并提供了多种实现类,适用于不同场景。

Java 中的 Deque(Double-Ended Queue的缩写) 接口是双端队列,它继承自 Queue 接口,其实现主要有 ArrayDeque 、 LinkedList 、ConcurrentLinkedDeque、BlockingDeque(接口)。

一、Queue 接口中核心方法

Queue 接口是继承自 Collection 接口,有如下的队列核心操作:

操作类型

方法

说明

异常情况

入队

add(e)

在队尾添加元素

队列满时抛出IllegalStateException

入队

offer(e)

在队尾添加元素(推荐)

队列满时返回false

出队

remove()

移除并返回队头元素

队列为空时抛出NoSuchElementException

出队

poll()

移除并返回队头元素(推荐)

队列为空时返回null

查看队头

element()

返回队头元素(不移除)

队列为空时抛出NoSuchElementException

查看队头

peek()

返回队头元素(不移除,推荐)

队列为空时返回null

二、Queue 的主要实现类

1.LinkedList(链表队列)
  • 底层结构:双向链表。
  • 特点
  • 实现了QueueDeque接口,可作为队列或双端队列使用。
  • 无容量限制(理论上受内存限制),不会出现队列满的情况。
  • 入队 / 出队操作效率为O(1),但因链表节点的内存开销,性能略低于数组实现。
  • 适用场景:需要动态扩容、不确定元素数量的场景。
Queue<Integer> queue = new LinkedList<>();
queue.offer(1);   // 入队
queue.offer(2);
System.out.println(queue.poll());  // 出队:1
System.out.println(queue.peek());  // 查看队头:2
2.ArrayDeque(双端队列)
  • 底层结构:动态扩容的循环数组。
  • 特点
  • 实现了Deque接口,支持两端入队 / 出队(兼具队列和栈的功能)。
  • 初始容量为 8,当元素满时自动扩容(翻倍)。
  • 入队 / 出队操作效率为O(1),数组结构减少了链表的指针开销,性能优于LinkedList
  • 适用场景:需要高效双端操作、频繁入队出队的场景(推荐作为队列 / 栈的首选)。
Queue<Integer> queue = new ArrayDeque<>();
queue.offer(10);
queue.offer(20);
System.out.println(queue.poll());  // 10
3.PriorityQueue(优先级队列)
  • 底层结构:基于二叉堆(小顶堆)。
  • 特点
  • 不遵循 FIFO,而是按元素优先级排序(默认自然顺序,可自定义Comparator)。
  • 队头始终是优先级最高的元素(如最小元素)。
  • 入队操作O(log n),出队操作O(log n)
  • 适用场景:需要按优先级处理任务的场景(如调度系统)
// 默认小顶堆(元素从小到大出队)
Queue<Integer> pq = new PriorityQueue<>();
pq.offer(3);
pq.offer(1);
pq.offer(2);
System.out.println(pq.poll());  // 1(最小元素)
System.out.println(pq.poll());  // 2// 自定义大顶堆(元素从大到小出队)
Queue<Integer> maxPq = new PriorityQueue<>((a, b) -> b - a);
maxPq.offer(3);
maxPq.offer(1);
maxPq.offer(2);
System.out.println(maxPq.poll());  // 3(最大元素)
4.BlockingQueue(阻塞队列)

BlockingQueue 是继承自 Queue 的子接口,增加了阻塞操作(当队列满 / 空时,线程会阻塞等待),专为多线程协作设计。

主要实现类有:

实现类

特点

ArrayBlockingQueue

基于数组的有界阻塞队列,容量固定,支持公平 / 非公平锁。

LinkedBlockingQueue

基于链表的阻塞队列,默认容量为Integer.MAX_VALUE

(可视为无界)。

SynchronousQueue

无容量队列,入队操作必须等待出队操作完成(同步传递元素)。

PriorityBlockingQueue

带优先级的无界阻塞队列,按优先级排序。

核心阻塞方法

  • put(e):队列满时阻塞,直到有空间。
  • take():队列为空时阻塞,直到有元素。
BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(2);
// 入队(满时阻塞)
new Thread(() -> {try {blockingQueue.put(1);blockingQueue.put(2);blockingQueue.put(3);  // 队列满,阻塞等待} catch (InterruptedException e) {e.printStackTrace();}
}).start();// 出队(空时阻塞)
new Thread(() -> {try {Thread.sleep(1000);System.out.println(blockingQueue.take());  // 1,唤醒入队线程} catch (InterruptedException e) {e.printStackTrace();}
}).start();

三、队列应用

  1. 任务调度:如线程池中的任务队列(ThreadPoolExecutor使用BlockingQueue存储待执行任务)。
  2. 消息队列:分布式系统中,用队列缓冲消息(如 Kafka、RabbitMQ 的底层思想)。
  3. 广度优先搜索(BFS):遍历树或图时,用队列存储待访问节点。
  4. 缓冲处理:如 IO 流中的缓冲区,平衡数据读写速度差异。
  • 单线程场景:优先用ArrayDeque(高效)或LinkedList(灵活)。
  • 优先级需求:用PriorityQueue
  • 多线程并发:非阻塞用ConcurrentLinkedQueue,阻塞用ArrayBlockingQueueLinkedBlockingQueue
  • 固定容量:用ArrayBlockingQueueArrayDeque(手动指定初始容量)。

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

相关文章:

  • OKHttp核心设计解析:拦截器与连接池的工作原理与实现机制
  • 做资源网站需要什么单页做网站教程
  • 实用程序:一键提取博客图片链接并批量下载的工具
  • 破解入门学习笔记题四十七
  • 登陆国外网站速度慢网站重构案例
  • 百日挑战——单词篇(第二十三天)
  • 基于Flask + ECharts的个人财务仪表盘 -(上个记账本的优化MAX)
  • Galois 理论 | 发展历程 / 基本定理的证明
  • 给定一个数组,如何用最小的比较次数获得最大最小值
  • 个人网站免费源码大全南宁seo管理
  • Linux服务器崩溃急救指南:快速诊断与恢复
  • 后端服务发现配置
  • wordpress建的手机网站合肥信息网
  • 我爱学算法之—— 字符串
  • 关于Function JS加密加密(仅于问题分析)
  • mysql基础——视图
  • win系统做网站wordpress侧边文本轮播图片
  • 免费微商城平台官网一直在做竞价的网站是不是不需要做seo
  • 输出纹波实测:ASP3605在不同输入与负载条件下的稳定性表现
  • RAG向量索引-HNSW Hierarchical Navigable Small World 介绍
  • 沈阳做网站的企业重庆房产网站建设
  • 让老版 IntelliJ IDEA 2020.1.4 支持 JDK 17 启动 springboot3 项目
  • 网站开发逻辑商丘网站建设求职简历
  • [Linux网络——Lesson1.初识计算机网络]
  • 电子电气架构全解析
  • 5G技术:推动数字经济的下一个革命性浪潮
  • 5G与AI赋能智能制造:未来生产的双重驱动力
  • 从工业互联网到智慧城市:5G与物联网的跨界融合
  • 5G NR PBCH与MIB技术介绍
  • 怎么查询网站的点击量招商网站建设全包