C/C++每日一练:实现一个环形队列

队列(queue)

         队列是一种先进先出(FIFO,First In First Out) 的数据结构,类似于排队的场景。最先进入队列的元素最先被处理,而后加入的元素则排在队列的末尾。

         常见的队列操作:

  • 入队 (enqueue):将元素加入队列尾部。
  • 出队 (dequeue):移除队列头部的元素。

环形队列 (Circular Queue)

         环形队列是对普通队列的一种优化,它使用一个固定大小的数组来存储数据。当队列的尾部到达数组末尾时,它会“环绕”到数组的开头,从而高效地利用空间。这种设计特别适合用于空间有限的情况,比如内存受限的嵌入式系统或循环缓冲区。

         环形队列具有以下特性:

  • 队尾指针插入元素时,如果到达数组末尾,会回到数组的开头。
  • 队头指针移除元素时,也会随着队列的移动进行循环。

         通过这种方式,环形队列可以避免普通队列中可能存在的空间浪费问题。

演示环形队列的操作过程

         下面将展示从空队列到满队列,以及出队和入队操作动态管理队列中的元素的状态变化。希望能狗帮助理解!

         这里用数组演示,先介绍两个概念和数组初始化状态:

  • front:指向队列的头部,即队列中第一个有效元素的位置。出队时将移除这个位置的元素,然后 front 移动到下一个有效元素的位置。
  • rear:指向队列的尾部,即下一个可插入元素的位置。在插入新元素后,rear 将向后移动。

         初始状态:

  • 数组:[ _, _, _, _, _ ],
  • 数组当前容量:0
  • front 0
  • rear 0

         环形队列的操作演示如下所示:

1. 入队元素 1

        当前状态:

  • 数组:[ 1, _, _, _, _ ]
  • front = 0
  • rear = 1(rear 移动到下一个位置)
2. 入队元素 2

        当前状态:

  • 数组:[ 1, 2, _, _, _ ]
  • front = 0
  • rear = 2(rear 移动到下一个位置)
3. 入队元素 3

        当前状态:

  • 数组:[ 1, 2, 3, _, _ ]
  • front = 0
  • rear = 3(rear 移动到下一个位置)
4. 出队元素 1

        当前状态:

  • 数组:[ _, 2, 3, _, _ ]( front 之前指向位置上的 1 已被移除)
  • front = 1(出队后,front 会移动到下一个有效元素的位置(2),此时数组中的 2 为队列的第一个有效元素的位置)
  • rear = 3
5. 入队元素 4

        当前状态:

  • 数组:[ _, 2, 3, 4, _ ]
  • front = 1
  • rear = 4(rear 移动到下一个位置)
6. 入队元素 5

        当前状态:

  • 数组:[ _, 2, 3, 4, 5 ]
  • front = 1
  • rear = 0(rear 环绕到数组的开头,此时队列已满)
7. 入队元素 6(此时队列已满,入队失败)

        当前状态:

  • 数组:[ _, 2, 3, 4, 5 ]
  • front = 1
  • rear = 0
        根据队列满的判断条件,此时队列已满,无法入队。

       注意:也许你会发现,此时数组中索引 0 的位置空缺(在第4步已被出队),但实际上不能再插入新元素,因为队列已满。这是因为,在环形队列的实现中,队列会始终存在一个空缺位置,这是为了区分队列是满还是空的状态。这就是为什么即使队列看起来还有一个空位置,实际上它已经被视为满了。如果不保留一个空缺位置,当 front = rear 时,无法区分是队列空还是队列满

        在环形队列中,当队列满时,rear 和 front 的位置关系非常特殊。此时,rear 指向下一个可插入位置,而这个位置正好是 front 的当前位置。因此,队列满的判断条件是:

(rear + 1) % capacity == front

        具体例子:假设队列容量为 5,当前状态为:

  • 数组:[ 6, 2, 3, 4, 5 ]
  • front = 1(指向元素 2)
  • rear = 0(下一次插入应该在数组开头的 6 的位置)

        判断队列是否满

计算条件: (rear + 1) % capacity == front

得:rear + 1 = 0 + 1 = 1

(1 % 5) == 1,条件成立,说明队列已满。

题目要求

         实现一个环形队列(Circular Queue)。该队列需要支持如下基本操作:

  • 入队 (enqueue): 向队列的末尾插入元素。
  • 出队 (dequeue): 从队列的头部移除元素。
  • 获取队头元素 (front): 返回队列头部的元素。
  • 获取队尾元素 (rear): 返回队列尾部的元素。
  • 检查队列是否为空 (isEmpty): 判断队列是否为空。
  • 检查队列是否已满 (isFull): 判断队列是否已满。

         环形队列是一种利用数组实现的队列。与普通队列不同的是,环形队列的末尾与头部相连,使得当队列满时,队列可以循环使用。队列中有两个指针,分别指向头部和尾部,以方便入队和出队操作。

做题思路

         环形队列的核心在于其“环形”特性。要实现这一特性,需要对数组的下标进行循环处理。使用两个指针 front 和 rear 来标记队列的头部和尾部,同时注意如何处理这些指针的更新逻辑。

  • 入队操作:在尾部插入元素时,rear 指针应该向前移动,并在数组末尾时绕回到头部。需要注意队列已满的情况。
  • 出队操作:在头部移除元素时,front 指针也应该向前移动,并同样处理绕回到头部的情况。
  • 判断队列满和空的条件:环形队列已满的条件是 (rear + 1) % capacity == front,而空的条件是 front == rear。

过程解析

  1. 初始化:分配一个固定大小的数组,用于存储队列中的元素。初始化 front 和 rear ,并提供队列的容量。
  2. 入队操作:在队尾插入新元素,并更新 rear 指针。
  3. 出队操作:移除队头元素,并更新 front 指针。
  4. 辅助操作:实现队列满、队列空、返回队头元素和队尾元素的操作。

运用到的知识点

  • 数组操作:环形队列本质上是基于数组的实现,了解如何使用数组的下标进行操作是关键。
  • 模运算:使用模运算 (%) 来实现队列的“环形”效果,保证队列头尾指针可以循环使用数组的空间。
  • 队列操作:理解队列的基本操作,包括入队、出队、判断空满等,是数据结构中的基础知识。
  • 环形结构理解
  • 边界条件处理

示例代码

C 实现:

#include <stdio.h> // 引入标准输入输出库,用于打印输出  
#include <stdbool.h> // 引入布尔类型库,用于表示真/假  

#define MAX_SIZE 5 // 定义队列的最大容量为5  

// 定义循环队列的结构体  
typedef struct {
    int data[MAX_SIZE];  // 队列数组,用于存储队列元素  
    int front;           // 队头指针,指向队列的第一个元素  
    int rear;            // 队尾指针,指向队列的下一个插入位置(非当前最后一个元素)  
} CircularQueue;

// 初始化队列,设置队头和队尾指针为0  
void initQueue(CircularQueue* queue) {
    queue->front = 0;
    queue->rear = 0;
}

// 判断队列是否为空,当队头和队尾指针相等时为空  
bool isEmpty(CircularQueue* queue) {
    return queue->front == queue->rear;
}

// 判断队列是否已满,根据循环队列的特性,当队尾指针的下一个位置是队头指针时,队列已满  
bool isFull(CircularQueue* queue) {
    return (queue->rear + 1) % MAX_SIZE == queue->front;
}

// 入队操作,将元素插入队列尾部  
bool enqueue(CircularQueue* queue, int value) {
    if (isFull(queue)) { // 如果队列已满,打印提示信息并返回false  
        printf("队列已满,无法入队。\n");
        return false;
    }
    queue->data[queue->rear] = value;             // 在队尾插入元素  
    queue->rear = (queue->rear + 1) % MAX_SIZE;   // 更新队尾指针,实现循环  
    return true;
}

// 出队操作,从队列头部移除元素  
bool dequeue(CircularQueue* queue, int* value) {
    if (isEmpty(queue)) { // 如果队列为空,打印提示信息并返回false  
        printf("队列为空,无法出队。\n");
        return false;
    }
    *value = queue->data[queue->front];           // 取出队头元素  
    queue->front = (queue->front + 1) % MAX_SIZE; // 更新队头指针,实现循环  
    return true;
}

// 获取队头元素,如果队列为空,打印提示信息并返回-1  
int front(CircularQueue* queue) {
    if (isEmpty(queue)) {
        printf("队列为空,无法获取队头元素。\n");
        return -1; // 返回一个无效值  
    }
    return queue->data[queue->front];
}

// 获取队尾元素,注意队尾指针指向的是下一个插入位置,因此需要回退一个位置  
int rear(CircularQueue* queue) {
    if (isEmpty(queue)) {
        printf("队列为空,无法获取队尾元素。\n");
        return -1; // 返回一个无效值  
    }
    // 计算实际队尾元素的索引位置  
    int rearIndex = (queue->rear - 1 + MAX_SIZE) % MAX_SIZE;
    return queue->data[rearIndex];
}

// 测试代码  
int main() 
{
    CircularQueue queue; // 声明一个循环队列变量  
    initQueue(&queue);   // 初始化队列  

    enqueue(&queue, 10); // 入队操作,插入元素10  
    enqueue(&queue, 20); // 入队操作,插入元素20  
    enqueue(&queue, 30); // 入队操作,插入元素30  
    enqueue(&queue, 40); // 入队操作,插入元素40  

    printf("队头元素: %d\n", front(&queue)); // 打印队头元素  
    printf("队尾元素: %d\n", rear(&queue));  // 打印队尾元素  

    int value;
    dequeue(&queue, &value); // 出队操作,移除队头元素,并打印出队元素  
    printf("出队元素: %d\n", value);

    enqueue(&queue, 50); // 入队操作,插入元素50  

    printf("队头元素: %d\n", front(&queue)); // 再次打印队头元素  
    printf("队尾元素: %d\n", rear(&queue));  // 再次打印队尾元素  

    return 0;
}

C++ 实现:

#include <iostream> // 引入输入输出流库,用于输入输出操作  
using namespace std; // 使用std命名空间,避免每次调用标准库时都需要加std::前缀  
  
#define MAX_SIZE 5 // 定义队列的最大容量为5  
  
// 定义循环队列类  
class CircularQueue {  
private:  
    int data[MAX_SIZE];  // 队列数组,用于存储队列中的元素  
    int front;           // 队头指针,指向队列的第一个元素  
    int rear;            // 队尾指针,指向队列中下一个将要插入元素的位置(非当前最后一个元素)  
  
public:  
    // 构造函数,初始化队列的队头和队尾指针  
    CircularQueue() {  
        front = 0;  
        rear = 0;  
    }  
  
    // 判断队列是否为空  
    bool isEmpty() {  
        return front == rear; // 当队头和队尾指针相等时,队列为空  
    }  
  
    // 判断队列是否已满  
    bool isFull() {  
        return (rear + 1) % MAX_SIZE == front; // 根据循环队列的特性,当队尾指针的下一个位置是队头指针时,队列已满  
    }  
  
    // 入队操作,将元素插入队列尾部  
    bool enqueue(int value) {  
        if (isFull()) { // 如果队列已满,打印提示信息并返回false  
            cout << "队列已满,无法入队。" << endl;  
            return false;  
        }  
        data[rear] = value;              // 在队尾插入元素  
        rear = (rear + 1) % MAX_SIZE;    // 更新队尾指针,实现循环  
        return true; // 入队成功返回true  
    }  
  
    // 出队操作,从队列头部移除元素  
    bool dequeue(int &value) {  
        if (isEmpty()) { // 如果队列为空,打印提示信息并返回false  
            cout << "队列为空,无法出队。" << endl;  
            return false;  
        }  
        value = data[front];             // 取出队头元素  
        front = (front + 1) % MAX_SIZE;  // 更新队头指针,实现循环  
        return true; // 出队成功返回true  
    }  
  
    // 获取队头元素  
    int getFront() {  
        if (isEmpty()) { // 如果队列为空,打印提示信息并返回一个无效值  
            cout << "队列为空,无法获取队头元素。" << endl;  
            return -1;  // 返回一个无效值  
        }  
        return data[front]; // 返回队头元素  
    }  
  
    // 获取队尾元素  
    int getRear() {  
        if (isEmpty()) { // 如果队列为空,打印提示信息并返回一个无效值  
            cout << "队列为空,无法获取队尾元素。" << endl;  
            return -1;  // 返回一个无效值  
        }  
        int rearIndex = (rear - 1 + MAX_SIZE) % MAX_SIZE; // 计算实际队尾元素的索引位置  
        return data[rearIndex]; // 返回队尾元素  
    }  
};  
  
// 测试代码  
int main() 
{  
    CircularQueue queue; // 声明一个循环队列对象  
  
    queue.enqueue(10); // 入队操作,插入元素10  
    queue.enqueue(20); // 入队操作,插入元素20  
    queue.enqueue(30); // 入队操作,插入元素30  
    queue.enqueue(40); // 入队操作,插入元素40  
  
    cout << "队头元素: " << queue.getFront() << endl; // 打印队头元素  
    cout << "队尾元素: " << queue.getRear() << endl;  // 打印队尾元素  
  
    int value;  
    queue.dequeue(value); // 出队操作,移除队头元素,并将出队元素的值赋给value  
    cout << "出队元素: " << value << endl; // 打印出队元素的值  
  
    queue.enqueue(50); // 入队操作,插入元素50  
  
    cout << "队头元素: " << queue.getFront() << endl; // 再次打印队头元素  
    cout << "队尾元素: " << queue.getRear() << endl;  // 再次打印队尾元素  
  
    return 0; // 程序正常结束  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/897090.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第二届中国楚域品牌文化创新发展大会暨楚域尚品发布会在汉圆满落幕

10 月 19 日&#xff0c;“第二届中国楚域品牌文化创新发展大会暨楚域尚品发布会”在武汉市光谷九通海源大酒店隆重举行。本次大会由中国商业文化研究会传承创新工作委员会、楚域品牌文化传承创新工作委员会、华夏品牌文化创新发展大会组委会主办&#xff0c;湖北省企业文化促进…

python爬虫简易入门示例

版本环境 win11python 3.12.4 目标&#xff1a;爬取https://gitee.com/explore的列表内容&#xff0c;并写入txt文本 效果 开始 1.安装依赖 pip install requests beautifulsoup42.编写代码&#xff0c;如下&#xff0c;详见注释 import requests from bs4 import Beauti…

【PFGA】二选一数选器

文章目录 前言一、实验原理二、实验过程三、实验结果参考文献 前言 进行 verilog FPGA 实验 一、实验原理 二、实验过程 三、实验结果 代码 module mux21(input s,input a,input b,output reg y); always(s or a or b) beginif (~s) beginy<a;end else beginy<…

ollama+ollama-webu在windos上部署的教程

ollamaollama-webu在windos上部署的教程 一、需要准备的环境和代码二、开始部署1. 修改系统变量&#xff1a; 常见问题 首先介绍一下ollama&#xff1a; Ollama 是一种为快速大规模语言模型推理所设计的框架和平台。它旨在帮助用户通过高效的方式运行和管理大型语言模型&#x…

使用AITemplate和AMD GPU的高效图像生成:结合Stable Diffusion模型

Efficient image generation with Stable Diffusion models and AITemplate using AMD GPUs 2024年1月24日&#xff0c;作者是[Douglas Jia] Stable Diffusion 已成为图像生成领域的突破性进展&#xff0c;帮助用户将文本描述转化为引人入胜的视觉输出。 Stable Diffusion 的…

SAP_通用模块-MASS批量操作技巧(二)

业务背景&#xff1a; 前两天写了一篇关于MASS批量操作的文档&#xff0c;当时测试批量扩充物料视图的时候失败了&#xff0c;就没记录进去&#xff0c;然后手头上刚好有一个需求&#xff0c;就是物料已经有基本视图等相关信息的情况下&#xff0c;需要扩充相关的物料视图。方法…

光纤光学——弱导光纤与线偏振模

一、基本思想 弱导光纤&#xff1a;n1≈ n2 , k0n1 ≈ k0n2&#xff0c;亦即&#xff1a; k0n1 ≈ k0 n2 ≈ 光线与纤轴的夹角小&#xff1b;芯区对光场的限制较弱&#xff1b; 消逝场在包层中延伸较远。 弱导光纤场的特点&#xff1a; HEι1,m模式与EHι-1,m色散曲线相近…

1024程序员节·城市聚会·西安,它来了

活动名称 CSDN 1024程序员节城市聚会西安 活动主题 智能进化&#xff1a; 开发者在AI时代的工作与生活变革 活动背景 CSDN一年一度的1024程序员节城市聚会&#xff08;西安站&#xff09;是一场专为程序员打造的盛会。这个活动旨在为西安的开发者们提供一个交流技术、分享…

每日OJ题_牛客_数组变换_贪心+位运算_C++_Java

目录 牛客_数组变换_贪心位运算 题目解析 C代码1暴力 C代码2位运算 Java代码位运算 牛客_数组变换_贪心位运算 数组变换__牛客网 (nowcoder.com) 描述&#xff1a; 牛牛有一个数组&#xff0c;里面的数可能不相等&#xff0c;现在他想把数组变为&#xff1a;所有…

MySQL数据库和表的基本操作

目录 一、数据库的基础知识 背景知识 数据库的基本操作 二、数据类型 字符串类型 数值类型 日期类型 三、表的基本操作 创建表 查看表结构 查看所有表 删除表 一、数据库的基础知识 背景知识 MySQL是一个客户端服务器结构的程序 主动发送数据的这一方&#xff0c;…

【Java】集合补充

常见基础集合汇总 数据结构&#xff1a;栈 数据结构分为&#xff1a; &#xff08;1&#xff09;逻辑结构 &#xff1a;--》思想上的结构--》卧室&#xff0c;厨房&#xff0c;卫生间 ---》线性表&#xff08;数组&#xff0c;链表&#xff09;&#xff0c;图&#xff0c;树&…

近期股市热潮,现有架构模块下金融交易系统如何应对“冲击”?优化思路如下

近期股市热情高涨&#xff0c;激增的交易量挑战的不止是券商&#xff0c;还有交易系统的基础架构是否稳固。9月底&#xff0c;股市牛抬头&#xff0c;瞬时的高并发量一把“撞”崩多家券商的交易应用系统&#xff0c;导致交易停滞。 在这场资本盛宴背后&#xff0c;稳定、高效、…

一家异业联盟平台 两年百亿销售额怎么做到的?

近年来&#xff0c;互联网领域涌现了一颗耀眼的新星——“上海我店”&#xff0c;该平台短时间内交易额突破百亿大关&#xff0c;且用户数量在上月实现了近百万的增长。这一迅猛的扩张速度&#xff0c;自然吸引了众多商家的目光。不过&#xff0c;随着其影响力的提升&#xff0…

[自动化测试:Selenium]:环境部署和Webdriver的使用

文章目录 修改安装源打开Python Packages。点击梅花按钮。在弹出的对话框中&#xff0c;填入Name&#xff08;随便填&#xff09;&#xff0c;Repository URL&#xff0c;选择下列的源&#xff0c;一般先选择清华源按OK确认。配置完成 安装seleniumFile→Settings→Project&…

为你的网站增加点灵性:随系统变色

&#x1f33b; 前言 网站切换主题色已经是非常常见的功能了&#xff0c;提供浅色和暗色两种色调可以满足用户的使用习惯&#xff0c;帮助这些用户获得更好的访问体验。但是只能用户手动切换主题。 那如果用户已经将系统切换到了深色模式&#xff0c;当他们打开我们网站的时候…

虚拟机网络设置为桥接模式

1、打开VMware Workstation Pro&#xff0c;点击“虚拟机—设置”&#xff0c;进入虚拟机设置页面 2、点击“网络适配器”&#xff0c;网络连接选择桥接模式 3、点击“编辑—虚拟网络编辑器”&#xff0c;进入虚拟网络编辑器页面 4、选择桥接模式&#xff0c;并选择要桥接到的…

有趣的css - 跷跷板加载动画

大家好&#xff0c;我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;今天分享的是使用 css 模拟一个跷跷板效果的加载动画效果。 《有趣的css》系列最新实例通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面…

YOLO目标检测

文章目录 一、含义二、与传统检测对比1.one-stage的优缺点2.two-stage的优缺点 三、MAP指标1.基本概念2.计算方法3.指标意义 一、含义 YOLO&#xff08;You Only Look Once&#xff09;是一种基于深度学习的目标检测算法&#xff0c;由Joseph Redmon等人于2016年提出。它的核心…

基于STM32的多功能MP3播放器

基于STM32的多功能MP3播放器 基于STM32的多功能MP3播放器一、项目背景与意义二、系统设计与实现2.1 硬件设计2.2 软件设计2.3 系统调试 三、功能实现与展望四、结论五、附录 基于STM32的多功能MP3播放器 在数字化时代&#xff0c;多功能播放器已成为我们生活中不可或缺的一部分…

Aurora 4.6.2 | 第三方谷歌商店,无需谷歌框架

Aurora Store是谷歌Play Store的一个非官方FOSS客户端&#xff0c;通常称为‘极光商店’&#xff0c;设计优雅。Aurora Store不仅可以下载、更新和搜索Play Store等应用程序&#xff0c;还为用户提供了新功能。应用提供范围广泛的游戏和应用程序&#xff0c;包括音乐流媒体、购…