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

队列的顺序结构——循环队列——入队

//入队
/**
 * @brief 入队操作,将元素插入到队列尾部
 *
 * 该函数用于将一个元素 e 插入到队列 Q 的尾部。
 * 在插入元素之前,会检查队列是否已满,如果已满则输出提示信息并返回 0。
 * 如果队列未满,则将元素插入到队列尾部,并更新队列的尾指针,最后返回 1 表示插入成功。
 *
 * @param Q 指向队列的指针
 * @param e 要插入的元素
 * @return int 插入成功返回 1,队列已满返回 0
 */
int equeue(Queue *Q, ElemType e)
{
    // 检查队列是否已满,使用取模运算处理循环队列情况
    if ((Q->rear + 1) % MAXSIZE == Q->front)
    {
        // 若队列已满,输出提示信息
        printf("满了\n");
        // 返回 0 表示插入失败
        return 0;
    }
    // 将元素 e 插入到队列的尾部
    Q->data[Q->rear] = e;
    // 更新队列的尾指针,使用取模运算确保尾指针在队列范围内循环
    Q->rear = (Q->rear + 1) % MAXSIZE;
    // 返回 1 表示插入成功
    return 1;
}

为啥要用取模运算?为啥这样算,你咋知道要用取模运算来判断,来写?

  1. 实现循环效果:循环队列的特点是首尾相连形成一个环状结构。取模运算可以很好地模拟这种循环行为。例如,当 rear 指向数组末尾时,rear + 1 会超出数组范围,但通过取模运算后会回到数组开头,完美地实现了"循环"。

  2. 简化边界处理:如果不使用取模运算,我们需要额外的条件语句来处理 rear 到达数组末尾的情况。使用取模后,不需要特殊处理,自然就能正确处理边界情况。

  3. 保持 O(1) 时间复杂度:取模运算的时间复杂度是 O(1),不会影响整体操作的效率。

  4. 统一计算方式:无论是检查队满还是更新 rear 指针,都使用相同的取模运算,使代码更加简洁和一致。

  5. 避免溢出错误:在检查队满的条件下,(Q->rear + 1) % MAXSIZE 不会导致整型溢出,因为结果始终在 [0, MAXSIZE-1] 范围内。

  1. 取模运算用于处理循环队列
    • 在循环队列中,队列的存储空间是有限的且被视为循环使用的。例如,假设队列的最大容量为MAXSIZE,当队列的尾指针rear到达MAXSIZE - 1后,如果再插入元素,尾指针应该回到队列的起始位置(即0)。
    • 取模运算(Q->rear + 1)%MAXSIZE可以很方便地实现这种循环效果。当rearMAXSIZE - 1时,(rear+1)%MAXSIZE就会得到0,使得尾指针能够正确地循环到队列的开头。
  2. 判断队列已满的原理
    • 对于循环队列,判断队列已满的条件是(Q->rear + 1)%MAXSIZE==Q->front。这是因为当队列满时,尾指针的下一个位置(即rear+1,但要考虑循环,所以用取模运算)就是头指针的位置。
    • 如果不使用取模运算,在处理循环队列时就很难准确判断队列是否已满以及尾指针的正确更新位置。
  3. 如何确定使用取模运算
    • 从循环队列的特性出发,由于队列的存储空间被循环使用,普通的线性操作无法满足其需求。
    • 取模运算在数学上正好能够实现这种循环的数值计算。在处理循环结构的索引(如循环队列中的指针)时,取模运算是一种常见且有效的方法来确保索引在有限的范围内循环。

为了彻底理解这个条件,我们通过一个具体例子逐步拆解:


假设队列最大容量 MAXSIZE = 5
此时队列的实际可用空间是 4(因为循环队列需要牺牲一个空间来区分队空和队满)。


场景1:初始空队列

  • front = 0(队头指针)
  • rear = 0(队尾指针)
  • 队列状态:空队列(front == rear)。

复制代码

索引:0   1   2   3   4  
数据:[空][空][空][空][空]
       ↑front  
       ↑rear

场景2:插入4个元素(队列未满)

  • 依次插入元素A、B、C、D,此时:
    front = 0rear = 4(最后一个元素在索引3,rear指向下一个空位索引4)。

复制代码

索引:0    1    2    3    4  
数据:[A]  [B]  [C]  [D]  [空]
       ↑front            ↑rear
  • 此时队列未满
    (rear + 1) % MAXSIZE = (4 + 1) % 5 = 0
    而 front = 0,所以 (rear+1) % MAXSIZE == front → 队列满(但实际队列还能插入一个元素?矛盾吗?)

关键点
循环队列强制牺牲一个空间来区分队空和队满。虽然此时物理上有空位(索引4),但逻辑上认为队列已满(因为再插入元素会导致front == rear,与队空条件冲突)。


场景3:尝试插入第5个元素(触发队满)

  • 插入元素E到索引4,此时:
    rear = (4 + 1) % 5 = 0
    此时 front = 0rear = 0 → 与队空条件 front == rear 冲突,无法区分空和满。

复制代码

索引:0    1    2    3    4  
数据:[A]  [B]  [C]  [D]  [E]
       ↑front  
↑rear

因此必须牺牲一个空间
队列实际最多存储 4 个元素(当 MAXSIZE = 5),此时条件 (rear + 1) % MAXSIZE == front 能正确判断队满。


最终条件解析

(Q->rear + 1) % MAXSIZE == Q->front 的含义:

  • 物理意义:队尾指针的下一个位置(循环意义下)是否与队头指针重合。
  • 目的:通过牺牲一个存储空间,避免队空和队满的判断条件冲突。
  • 结果:当队列剩余一个空位时,即认为队列已满。

相关文章:

  • 【零基础到精通Java合集】第十一集:List集合框架与泛型
  • 【练习】【链表】力扣热题100 2. 两数相加
  • 鸿蒙开发中,数据持久化之Transaction(事务)的概念及应用
  • C语言基础要素(010):复合赋值
  • Android 14 - HDMI_CEC架构分析
  • Go语言学习笔记(六)——标准库
  • 量子算法:英译名、概念、历史、现状与展望?
  • SparkStreaming之04:调优
  • WPF高级 | WPF 应用程序部署与发布:确保顺利交付到用户手中
  • 深入 PipeWire
  • Lua如何实现面向对象的三大特性?
  • Linux第五讲----gcc与g++,makefile/make
  • Halcon 算子-承接车牌识别
  • FPGA标准库-Open Logic
  • 376_C++_云透传,板端负责处理透传数据的API函数,用于实现客户端对设备内部接口的访问(VMS把数据直接传给板端内部)
  • 音频3A测试--AGC(自动增益)和NS(降噪)测试
  • 【监控】使用Prometheus+Grafana搭建服务器运维监控面板(含带BearerToken的Exporter配置)
  • 2024北京理工大学计算机复试上机真题
  • 《AJAX:前端异步交互的魔法指南》
  • 828华为云征文|使用sysbench对Flexus X实例对mysql进行性能测评
  • 天成信息网站建设自助建站平台/搜索广告
  • 网站开发设计工程师工作前景/集合竞价口诀背熟6句
  • 领卷网站怎么做的/搜索引擎优化
  • 官方网站minecraft/seo顾问是干什么
  • 无锡制作网站公司/同城推广引流平台
  • 制作网站详细步骤/企业营销型网站建设