数据结构与基础算法-环形队列

article/2025/9/28 0:23:34

一、什么是环形队列。

其实在内存上并没有所谓的环形队列,环形队列只是基于数组线性空间来实现。

环形队列优点

  1. 避免假溢出现象。(因为在数组里,头尾指针只增加不减少,被删元素的空间再也不能被重新利用。会造成尾指针已经到达了队列的最后一位,而头指针前面没有满的情况。)
  2. 广泛用于网络数据的收发。和不同程序之间的数据交换。
  3. 首尾相连的FIFO数据结构,采用数据的线性空间,能快速的知道队列是否满或者空。

二、环形队列的实现。

其实环形队列的实现核心点就是处理队列尾指针达到最后一位该怎么处理。
本文提供的方案是在队列的尾指针达到最后一位时,将其指向0的位置,采用取模(%)运算进行实现。

首先声明属性,其中rear属性需要重点关注,非常重要,它指向队尾元素的后一位,预留了一个数据空间作为一个约定。

    /** 头 指向当前元素*/private int front;/** 尾 指向队尾元素的后一位 相当于空了一位 区分队列空和队列满*/private int rear;/** 最大容量 (实例化是传入4,即代表该队列只会存放3个数据,有一个空余约定) */private int maxSize;private int[] arr;

创建构造器:

public CircleArrayQueue(int maxSize){this.maxSize = maxSize;arr = new int[maxSize];}

几个比较重要的方法:

1. 判断队列是否为空:

在这里插入图片描述
在这里插入图片描述

头指针=尾指针说明队列为空。

	public Boolean isEmpty(){return rear == front;}

2. 判断队列是否为满(关键):

判断环形队列是否满之前需要知道,在环形队列中front头指针能不能和普通队列中的头指针一样,伴随数据的写入而一直进行着自增?答案是不行。设计环形队列的初衷便是可以重复利用已经删除数据的空间,避免假溢现象的出现。
而且在判断一个环形队列是否满时,只需要关心尾指针以及头指针的关系即可。头指针必定会跟随着数据的写入而进行着自增,这样在队列满时就会出现和队列为空时一样的情况。都为front = rear,即头指针 = 尾指针,那么我们无法分辨究竟是队列满还是对列空,存在一定的歧义。
所以我们需要用一种新的方式来判断队列是否为满,同时与队列是否为空做出区别。

本文采用取模运算:
在这里插入图片描述

柱状图也许不方便理解环形队列已经满的情况。

通过下图可以看到在队列已经满的情况下rear指向了最后一个元素,但其实还有一个空的位置,这个时候需要将rear+1,空出来的位置是作为一个约定存在,是动态变化的。而在环形队列中数据一直写入会造成尾指针rear一直自增造成数组越界。所以需要加下标控制在一个范围以内,而在本文中,这个范围就是头指针front至最大容量maxSize的范围。而取模操作恰好能满足这个需求点。

例如:

  1. 在front = 0,rear = 3,maxSize = 7的情况下,(rear + 1) % maxSize = 0.6,而0.6 != 0,所以数组没有满,依然可以存储数据。
  2. 在front = 0,rear = 6,maxSize = 7的情况下,(rear + 1) % maxSize =0,而0 = 0,数组已经存满。

在这里插入图片描述

取模的意义在于将arr下标控制在front和maxSize之间,如果不取模,rear会一直加1,超出队列容量。

    public Boolean isFull(){return (rear + 1) % maxSize == front;}

3. 往环形数组添加数据的方法:

添加数据时先判断是否已满。
因为rear就是代表元素的位置,所以直接将要添加的数据放在尾指针rear处即可。
但是数据存放完毕后要将rear向后移动一位,根据前文说到的数组越界问题依旧存在,所以也要进行取模运算。

 public void add(int n){if(isFull()){throw new RuntimeException("队列已满。");}/** 将数据放在rear的位置 */arr[rear] = n;/** 因为是环形队列,需要考虑下标越界的问题,根据前面的逻辑,需要考虑取模。 */rear = (rear + 1) % maxSize;
}

4. 从环形数组获取数据的方法:

在从环形队列获取数据的时候要先判断队列是否为空。
获取数据时直接获取头指针front所在位置的数据即可。
剩下的逻辑与添加数据的逻辑一样,也得在取模后把指针往后移动一位。
不一样的一点在于取值的时候需要吧值保存在一个临时变量里面。

public int getNum(){if(isEmpty()){System.out.println("队列为空。");}/** 将当前位置的数据取出来,放在一个临时变量中,同时将指针移往后一位 */int num = arr[front];front = (front + 1) % maxSize;return num;}

5. 打印环形数组(重要):

打印环形队列有两点需要注意:

1、不能直接i< arr.length,现在是环形队列,是一直循环存储的。

环形队列有两种情况:

  1. front < rear (队列没满) —> maxSize - front = 有效数据个数。
  2. front > rear (队列已经存满) —> 0 + rear
  3. 两个式子合起来为:( maxSize - front ) + ( 0 + rear )
  4. 即maxSize - front + rear
  5. front的作用是提供一个起始位置。而取模是为了使下标数字一直在front和maxSize的范围以内。

2、打印的时候不能使用i = 0,要使用数据坐标作为遍历起始值。

public void showArr(){if(isEmpty()){throw new RuntimeException("队列为空。");}for (int i = front ; i < front + (maxSize - front + rear) % maxSize ; i++){System.out.println("i = " + i);/** i % maxSize的意义在于将i控制在front以及maxSize之间,避免数组越界。 */System.out.printf("arr[%d]=%d\n",i % maxSize,arr[i % maxSize]);}}

6. 获取头部数据:

直接根据下标获取即可。

 public int getHeadNum(){if(isEmpty()){System.out.println("队列为空。");}return arr[front];}

三、完整测试代码

package com.wyg.queue;import java.util.Scanner;/*** @program: CircleArrayQueueDemoV2* @description:环形队列* @author: wyg* @create: 2022/8/28**/
public class CircleArrayQueueDemoV2 {public static void main(String[] args) {/** maxSize = 4 代表该队列只能存放3个数据(有一个空余) */CircleArrayQueueV2 arrayQueue = new CircleArrayQueueV2(4);char key = ' ';Scanner scanner = new Scanner(System.in);Boolean b = true;while (b){System.out.println("s(show):显示队列");System.out.println("e(exit):退出队列");System.out.println("a(add):添加数据到队列");System.out.println("g(get):获取队列数据");System.out.println("h(head):显示队列头部数据");key = scanner.next().charAt(0);switch (key){case 's':arrayQueue.showArr();break;case 'e':scanner.close();b = false;break;case 'a':System.out.println("输入一个数。");int i = scanner.nextInt();arrayQueue.add(i);break;case 'g' :try{int num = arrayQueue.getNum();System.out.printf("取出的数据是%d\t",num);break;}catch (Exception e){System.out.println(e.getMessage());}case 'h' :try{int num = arrayQueue.getHeadNum();System.out.printf("取出的头部数据是%d\t",num);break;}catch (Exception e){System.out.println(e.getMessage());}break;}}}
}class CircleArrayQueueV2{/** 头 指向当前元素*/private int front;/** 尾 指向队尾元素的后一位 相当于空了一位 区分队列空和队列满*/private int rear;/** 最大容量 */private int maxSize;private int[] arr;public CircleArrayQueueV2(int maxSize){this.maxSize = maxSize;arr = new int[maxSize];}/** 取模的意义在于将arr下标控制在rear和maxSize之间,如果不取模,rear会一直加1,超出队列容量。 */public Boolean isFull(){return (rear + 1) % maxSize == front;}public Boolean isEmpty(){return rear == front;}public void add(int n){if(isFull()){throw new RuntimeException("队列已满。");}/** 将数据放在rear的位置 */arr[rear] = n;/** 因为是环形队列,需要考虑下标越界的问题,根据前面的逻辑,需要考虑取模。 */rear = (rear + 1) % maxSize;}public int getNum(){if(isEmpty()){System.out.println("队列为空。");}/** 将当前位置的数据取出来,放在一个临时变量中,同时将指针移往后一位 */int num = arr[front];front = (front + 1) % maxSize;return num;}public void showArr(){if(isEmpty()){throw new RuntimeException("队列为空。");}/** 打印环形队列需要注意;*  不能直接i< arr.length,现在是环形队列,是一直循环存储的。*  环形队列有两种情况:*  1. front < rear (队列没满) ---> maxSize - front = 有效数据个数。*  2. front > rear (队列已经存满) ---> 0 + rear*  3. 两个式子合起来为:( maxSize - front ) + ( 0 + rear )*  4. 即maxSize - front + rear*  5. front的作用是提供一个起始位置。而取模是为了使下标数字一直在front和maxSize的范围以内。*/for (int i = front ; i < front + (maxSize - front + rear) % maxSize ; i++){System.out.println("i = " + i);/** i % maxSize的意义在于将i控制在front以及maxSize之间,避免数组越界。 */System.out.printf("arr[%d]=%d\n",i % maxSize,arr[i % maxSize]);}}public int getHeadNum(){if(isEmpty()){System.out.println("队列为空。");}return arr[front];}
}

http://chatgpt.dhexx.cn/article/Ga8KVP2a.shtml

相关文章

无锁环形队列的几种高效实现

1.环形队列是什么 队列是一种常用的数据结构&#xff0c;这种结构保证了数据是按照“先进先出”的原则进行操作的&#xff0c;即最先进去的元素也是最先出来的元素.环形队列是一种特殊的队列结构&#xff0c;保证了元素也是先进先出的&#xff0c;但与一般队列的区别是&#…

队列——数组实现环形队列

【目的】 数组实现环形队列 【思路分析】 1. front 变量的含义做一个调整&#xff1a; front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素 front 的初始值 0 2. rear 变量的含义做一个调整&#xff1a;rear 指向队列的最后一个元素的后一个位置. …

常用数据结构 ——— 队列(环形队列和顺序队列)

目录 一、队列简介 二、顺序队列 三、环形队列 四、环形队列代码 1、队列结构体 2、队列初始化 3、判断队列是否为满 4、判断队列是否为空 5、将数据插入到队列中 6、读取队列中的数据 7、释放队列空间 8、功能测试 一、队列简介 队列只允许在队列头&#xff08;fr…

[Data structure]队列环形队列 | 一文带你彻底搞懂队列和环形队列(内附详细图解和代码实现)

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现 ⭐如果觉得文章写的不错&#xff0c;欢迎点个关注一…

C语言环形队列

#include <stdio.h> #define Len 6 unsigned char Input_Buff[6] {0}; //用户输入缓冲区 unsigned char Input_Num 0; //输入队列数据字节数 unsigned char Output_Num 0; //从队列取出的数据字节数 struct Queue {unsigned char Buffer[Len]; unsigned c…

【数据结构】队列、环形队列

目录 1.队列的概念及结构 2.队列的实现 3.队列的相关实现函数与源代码 3.1初始化队列 3.2 队尾入队列 3.3 队头出队列 3.4获取队列头部元素 3.5 获取队列队尾元素 3.6 获取队列中有效元素个数 3.7检测队列是否为空 3.8销毁队列 4.环形队列 4.1环形队列概念 …

java环形队列_数组实现环形队列Java

用数组实现环形队列的特点是高效。 能快速判断队列是否 满/空&#xff1b; 能快速存取数据。 因为简单高效&#xff0c;所以甚至在硬件中都实现了环形队列。 环形队列广泛应用于网络数据的收发&#xff0c;和不同应用间数据交换(内核和应用程序大量交换数据&#xff0c;从硬件接…

环形队列初步探讨

文章目录 前言一、环形队列二、环形队列基本操作二、示例总结 前言 最近使用队列的时候&#xff0c;在实现大数据整体平移的时候还是用了内存copy虽然暂时达到性能要求&#xff0c;但是总感觉很笨重&#xff0c;最后用环形队列重构了部分代码&#xff0c;效果还行。 一、环形队…

环形队列

介绍 环形队列是队列的一种特殊情况&#xff0c;也是基于队列的实现&#xff0c;队列是动态的集合&#xff0c;而环形队列则是固定长度的&#xff0c;当队列满时&#xff0c;则从队首删除元素。其原理基本和队列一致&#xff0c;都是实现先进先出的策略。 实现 先定义数据&…

C语言,环形队列

什么是环形队列&#xff1f; 环形缓冲区是一个非常典型的数据结构&#xff0c;这种数据结构符合生产者&#xff0c;消费者模型&#xff0c;可以理解它是一个水坑&#xff0c;生产者不断的往里面灌水&#xff0c;消费者就不断的从里面取出水。 那就可能会有人问&#xff0c;既然…

数据结构(10)---队列之环形队列

环形队列 文章目录 环形队列什么是环形队列循环队列的实现第一种实现第二种实现 什么是环形队列 环形队列也是队列的一种数据结构, 也是在队头出队, 队尾入队; 只是环形队列的大小是确定的, 不能进行一个长度的增加, 当你把一个环形队列创建好之后, 它能存放的元素个数是确定的…

数据结构--环形队列的介绍与实现

数据结构--环形队列实现 一、环形队列实现原理环形队列的几个判断条件 二、代码实现1.环形队列类&#xff08;CircleQueue&#xff09;2.环形队列类测试类3.程序运行结果4.完整代码 环形队列可以用数组实现&#xff0c;也可以使用循环链表实现.在使用数组实现循环队列的时候&am…

【数据结构(C语言描述)】环形队列

目录 一、基础知识二、数组实现环队2.1 初始化2.2 判断环队是否为空2.3 判断环队是否为满2.4 入队2.5 出队2.6 取队头元素2.7 取队尾元素2.8 销毁环队 三、链表实现环队3.1 初始化3.2 判断环队是否为空3.3 判断环队是否为满3.4 入队3.5 出队3.6 取队头元素3.7 取队尾元素3.8 销…

环形队列原理,全网最通俗易懂

队列是什么 队列是一种很常见的数据结构&#xff0c;满足先进先出的方式&#xff0c;如果我们设定队列的最大长度&#xff0c;那就意味着进队列和出队列的元素的数量实则满足一种动态平衡。 如果我们把首次添加入队列的元素作为一个一维坐标的原点&#xff0c;那么随着队列中…

Mysql 死锁和死锁的解决方案

最近在研究Mysql底层原理&#xff0c;研究到了死锁&#xff0c;感觉挺有意思&#xff0c;在这里和大家分享一下 前置知识&#xff1a;需要了解锁的种类&#xff0c;如表锁、行锁&#xff1b;行锁又分为记录锁、间隙锁、临键锁等等&#xff1b;什么情况下会加表锁&#xff0c;什…

mysql死锁演示

背景&#xff1a; 线上日志突然爆了有数据库死锁的日志。 通过以下语句查询数据库死锁的日志 SHOW ENGINE INNODB STATUS 通过 日志分析&#xff0c;看到了两条update语句并且是里面有子查询。还有两个表的更新顺序问题。 解决方案是&#xff1a;加了分布式锁&#xff0c;…

记录一次mysql死锁

一&#xff0c;死锁发现 项目中有一个接口包含更新操作1&#xff0c;后面发现更新失败&#xff0c;通过查看应用程序日志&#xff0c;发现发生了死锁 sql 1 如下 1.最初版本根据id为条件&#xff0c;更新&#xff08;plan_start_time 二级索引&#xff09; update tt_task …

深入MySQL死锁场景

总结死锁需满足以下条件&#xff1a; 2个或者2个以上的并发事务操作并发事务之间存在锁冲突锁冲突关系成环形 GAP锁和Insert的隐式锁&#xff0c;最容易导致死锁&#xff0c;以下分析从这俩典型场景开始。 1. 表结构 建立以下表作为场景验证&#xff0c;id为主键&#xff0…

mysql死锁查询语句

mysql 死锁:如何解决mysql死锁 可直接在mysql命令行执行&#xff1a;showengineinnodbstatus\G;查看造成死锁的sql语句&#xff0c;分析索引情况&#xff0c;然后优化sql然后showprocesslist;另外可以打开慢查询日志&#xff0c;linux下打开需在my.cnf的[mysqld]里面加上以下内…

MySQL死锁排查步骤

系列文章目录 第一章&#xff1a;sql_mode模式 第二章&#xff1a;optimize table、analyze table、alter table、gh-ost 第三章&#xff1a;InnoDB MVCC原理 第四章&#xff1a;sql语句执行过程 第五章&#xff1a;Percona Toolkit工具简介 第六章&#xff1a;MySQL索引 第七…