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

数据结构 实现循环队列的三种方法

队列(Queue)可以通过顺序结构实现,也可以通过链式结构实现。本文将讲解如何通过顺序结构实现队列,要通过顺序结构实现队列,通常建议使用循环队列的方式,也就是使用循环数组,它解决了普通数组实现队列时的空间浪费问题,同时保证了入队和出队操作的高效性(均为 O (1) 时间复杂度)

循环队列的核心思想:循环队列通过将数组视为一个环形结构,当队尾指针到达数组末尾时,会绕回数组的起始位置,从而充分利用数组空间

每有一个元素入队列,rear就移动到下一个位置,front的位置不变;每有一个元素出队列,front就移动到下一个位置,rear的位置不变。

不过这里唯一的问题是:怎么判断队列满和空?

对于空的情况:主要队列头和队列尾相遇,就是空的。

对于满的情况:

我们有三种做法:

1.定义一个size用于储存队列元素的个数;

2.添加标记,定义一个boolean类型变量,当队列头和队列尾相遇并且这个标记发生改变了,就说明满了;

3.浪费一个空间,当队列尾的下一个位置如果是队列头,那么就说明满了。

队列的常见方法:

public class MyCircularQueue {private int[] arr;private int front;private int rear;private int size;//构造方法public MyCircularQueue() {}//入队列public int enQueue(int data) {}//出队列public int deQueue() {}//从队首获取元素。如果队列为空,返回 -1 。public int Front() {}//获取队尾元素。如果队列为空,返回 -1 。public int Rear() {}//检查循环队列是否为空public boolean isEmpty() {}//检查循环队列是否已满public boolean isFull() {}
}

那么,接下来就分别用这三种方法实现循环队列。

1.方法一:定义size用于储存队列元素个数

构造方法

//构造方法public MyCircularQueue(int k) {this.arr = new int[k];}

检查循环队列是否已满

当front与rear相遇,并且size == 数组长度时,我们可以认为队列满了。

//检查循环队列是否已满public boolean isFull() {return (this.front == this.rear) && (this.size == this.arr.length);}

检查循环队列是否为空

当front与rear相遇时,此时并没有size == 数组长度,那么就说明队列是空的。

//检查循环队列是否为空public boolean isEmpty() {return this.front == this.rear;}

入队列

进行入队列操作前,需要先判断队列满了没有,如果满了,就返回-1,没满就正常入队列并且返回data。不过需要注意的是,rear移动到下一个位置不能简单的++,否则会造成越界访问,应当这样操作:rear = (rear + 1) % arr.length,这样就能是它实现循环。

//入队列public int enQueue(int data) {if (isFull()) {return -1;}this.arr[this.rear] = data;this.rear = (this.rear + 1) % this.arr.length;this.size++;return data;}

出队列

进行出队列操作前,需要先判断队列是不是空的,如果是,就返回-1,不是就正常出队列并且返回队头元素。这里需要注意front的移动,与rear的移动一样。

//出队列public int deQueue() {if (isEmpty()) {return -1;}int val = this.arr[this.front];this.front = (this.front + 1) % this.arr.length;this.size--;return val;}

从队首获取元素。如果队列为空,返回 -1 。

//从队首获取元素。如果队列为空,返回 -1 。public int Front() {if (isEmpty()) {return -1;}return this.arr[this.front];}

获取队尾元素。如果队列为空,返回 -1 。

这里需要注意,与获取队首元素有点区别,就是rear的位置,我们不能直接通过return arr[rear - 1] 去获取队尾元素,因为当rear位于0位置时,rear - 1 = -1,这是不合法的!因此我们需要对这两种情况分别处理。

//获取队尾元素。如果队列为空,返回 -1 。public int Rear() {if (isEmpty()) {return -1;}int index;if (this.rear == 0) {index = this.arr.length - 1;}else {index = this.rear - 1;}return this.arr[index];}

到此,我们就使用方法一实现了循环队列。完整代码如下:

/*** Created with IntelliJ IDEA.* Description:* User: mirac* Date: 2025-08-16* Time: 18:05*/
public class MyCircularQueue {private int[] arr;private int front;private int rear;private int size;//构造方法public MyCircularQueue(int k) {this.arr = new int[k];}//入队列public int enQueue(int data) {if (isFull()) {return -1;}this.arr[this.rear] = data;this.rear = (this.rear + 1) % this.arr.length;this.size++;return data;}//出队列public int deQueue() {if (isEmpty()) {return -1;}int val = this.arr[this.front];this.front = (this.front + 1) % this.arr.length;this.size--;return val;}//从队首获取元素。如果队列为空,返回 -1 。public int Front() {if (isEmpty()) {return -1;}return this.arr[this.front];}//获取队尾元素。如果队列为空,返回 -1 。public int Rear() {if (isEmpty()) {return -1;}int index;if (this.rear == 0) {index = this.arr.length - 1;}else {index = this.rear - 1;}return this.arr[index];}//检查循环队列是否为空public boolean isEmpty() {return this.front == this.rear;}//检查循环队列是否已满public boolean isFull() {return (this.front == this.rear) && (this.size == this.arr.length);}
}

其实这三种方法都是大同小异的,只不过判断队列空和满的条件不一样罢了,因此使用方法二和方法三实现循环队列就不再过多赘述。

2.方法二:添加标记

package demo;public class MyCircularQueue {private int[] arr;private int front;private int rear;private boolean mark = true;//构造方法public MyCircularQueue(int k) {this.arr = new int[k];}//入队列public int enQueue(int data) {if (isFull()) {return -1;}this.arr[this.rear] = data;this.rear = (this.rear + 1) % this.arr.length;//如果本次入队列使得front与rear相遇的话,使标记发生改变if (this.rear == this.front) {this.mark = false;}return data;}//出队列public int deQueue() {if (isEmpty()) {return -1;}int val = this.arr[this.front];this.front = (this.front + 1) % this.arr.length;this.mark = true;return val;}//从队首获取元素。如果队列为空,返回 -1 。public int Front() {if (isEmpty()) {return -1;}return this.arr[this.front];}//获取队尾元素。如果队列为空,返回 -1 。public int Rear() {if (isEmpty()) {return -1;}int index;if (this.rear == 0) {index = this.arr.length - 1;}else {index = this.rear - 1;}return this.arr[index];}//检查循环队列是否为空public boolean isEmpty() {return this.front == this.rear;}//检查循环队列是否已满public boolean isFull() {return (this.front == this.rear) && (this.mark == false);}
}

解释:方法二不再定义用于记录队列元素的size,而是定义一个标记位mark。mark的初始值为true,当front与rear相遇,并且mark发生改变了,就说明队列满了;仅仅front与rear相遇,就说明队列是空的。mark发生改变的2处地方:1.入队列:若队列未满,并且成功入队列后,如果rear移动后的位置 == front的位置,那么mark就发生改变,mark = false。2.出队列:若队列不空,并且成功出队列后,不管怎么样,mark = true。

3.方法三:浪费一个空间

package demo1;public class MyCircularQueue {private int[] arr;private int front;private int rear;//构造方法public MyCircularQueue(int k) {this.arr = new int[k + 1];}//入队列public int enQueue(int data) {if (isFull()) {return -1;}this.arr[this.rear] = data;this.rear = (this.rear + 1) % this.arr.length;return data;}//出队列public int deQueue() {if (isEmpty()) {return -1;}int val = this.arr[this.front];this.front = (this.front + 1) % this.arr.length;return val;}//从队首获取元素。如果队列为空,返回 -1 。public int Front() {if (isEmpty()) {return -1;}return this.arr[this.front];}//获取队尾元素。如果队列为空,返回 -1 。public int Rear() {if (isEmpty()) {return -1;}int index;if (this.rear == 0) {index = this.arr.length - 1;}else {index = this.rear - 1;}return this.arr[index];}//检查循环队列是否为空public boolean isEmpty() {return this.front == this.rear;}//检查循环队列是否已满public boolean isFull() {return (this.rear + 1) % this.arr.length == this.front;}
}

解释:方法三不需要size,也不需要mark,它通过浪费一个空间来判断空与满,这实际上是一种变相的标记位,它判断队列满的条件为:当rear的下一个位置 == front的位置时,就说明队列满了。需要注意的是,因为我们浪费了一个空间作为标记位,因此在构造方法中,数组的容量应当是 k + 1。

到此,三种实现循环队列的方法已经一一罗列,感谢您的观看,如有错误,还请指出,欢迎讨论!

http://www.xdnf.cn/news/17874.html

相关文章:

  • 如何在 MacOS 上安装 SQL Server
  • 搭建ktg-mes
  • 新手向:Python列表、元组、集合和字典的用法对比
  • MySQL的三大范式:
  • AI云电脑盒子技术分析——从“盒子”到“算力云边缘节点”的跃迁
  • 实现Android图片手势缩放功能的完整自定义View方案,结合了多种手势交互功能
  • Vue 3.5重磅更新:响应式Props解构,让组件开发更简洁高效
  • MQ积压如何处理
  • Markdown 生成 Gantt 甘特图
  • 使用js完成抽奖项目 效果和内容自定义,可以模仿游戏抽奖页面
  • 31 HTB Union 机器 - 中等难度
  • C:\Windows\WinSxS 目录详解
  • 【C++】标准库中用于组合多个值的数据结构pair、tuple、array...
  • AI搜索引擎下的内容优化新范式:GEO的关键技术解析
  • 二十七、动态SQL
  • RK3568 NPU RKNN(三):RKNN-ToolKit2模型构建与推理
  • 大模型教机器人叠衣服:2025年”语言理解+多模态融合“的智能新篇
  • PowerPoint和WPS演示放映PPT时如何禁止鼠标翻页
  • 玉米及淀粉深加工产业展|2026中国(济南)国际玉米及淀粉深加工产业展览会
  • Java 学习笔记(基础篇3)
  • 数据结构:构建 (create) 一个二叉树
  • 【数据结构入门】二叉树(2)
  • 内网穿透实战笔记 1panel 面板部署 frps,Windows 部署 frpc
  • Ubuntu永久配置 DNS
  • JavaScript 原型机制详解:从概念到实战(附个人学习方法)
  • 【Mysql语句练习】
  • linux 设备驱动的分层思想
  • 二分算法(模板)
  • week1-[顺序结构]大海
  • 9.对象介绍