循环队列

以下说法都是针对顺序存储结构实现的队列

先理清思路
(1)普通队列的元素满足:存储在数组的前n个单元 这一条件,因此队列元素出列时,由于队列元素都得向前移动,因此需要O(n)时间

(2)为了省去O(n)时间,引入front与rear指针,分别指向队头元素、队尾元素的下一个位置

而rear不是指向队尾元素而是指向其下一个位置的原因是:避免当只有一个元素时,队头和队尾重合使处理变麻烦;当遵循上述规则时出现 front = rear,则证明是空队列而不是只有一个元素

空队列

012345
front rear
↓ ↓

只有一个元素:

012345
frontrear
a1

(3)但(2)的结构有缺点,因为无需限制队列的元素必须存储在数组的前n个单元的条件,当队列中进行若干进队出队操作后,front、rear指针就会不断后移,直至rear指针越界时,是否说明队列已满,已无可用空间?答案当然是NO!因为front指针前的数组位置都是可用的,这种现象称为假溢出

012345
frontrear
a3a4a5a6

(4)解决假溢出的办法,就是后面满了,再从头开始,即头尾相接的循环。把队列的这种头尾相接的顺序存储结构称为循环队列
至此,循环队列应运而生。

那么问题又来了,按循环队列的规则实现后,(2)中提到的front=rear 就不一定是空队列,也有可能是rear指针循环回首部再后移,即队列为满。

那如何判断队列是空还是满呢?
办法一:最简单暴力的办法,设置一个标志变量flag,flag=0 为队列空,flag=1为队列满,当front==rear时,就可以根据flag来判断。

办法二:约定数组不能真正为满,即当队列满时,数组中还有一个空闲单元。

现在重点讨论第二种办法,队列满时有两种情况,
1:无出队操作(或者是多次轮回到初始状态也有可能),即front =0,rear=arr.size , 此时 front > rear

012345
frontrear
a1a2a3a4a5

2:有出队操作,且队尾满后,rear从头开始,此时front < rear

012345
rearfront
a7a3a4a5a6

所以为了综合以上两种情况,判断队列满的条件为:
(rear + 1)%QueueSize == front

重头戏来了,计算队列的长度公式是
(rear - front + QueueSize)% QueueSize

1:当front > rear 时,计算长度显然是 rear-front ,不多解释。
而此时 0 < rear-front < QueueSize, 显然符合(rear - front + QueueSize)% QueueSize

2:当front < rear 时,长度可拆分为两段
eg:

012345
rearfront
a7a3a4a5a6

一段是 rear指针前的数据长度,为 rear (由 rear - 0而来)

另一段是front指针至数组边界,为 QueueSize - front
从 ( (QueueSize - 1) - front ) +1 而来,其实就是求【front,QueueSize - 1】的长度,其中 QueueSize - 1 就是获取数组末尾的下标,

两段长度相加,就是 rear - front + QueueSize ,
此时 -QueueSize < rear - front < 0 < QueueSize ,
所以 0 < rear - front + QueueSize < QueueSize
因此 该结果 % QueueSize 也不会发生变化

有了以上的铺垫,再看公式
(rear - front + QueueSize)% QueueSize,就是为了整合两种情况。

而循环队列的入队出队操作也并不复杂,只要注意更新对应的front / rear 指针时,
不是简单的 +1 ,而是 (指针 + 1)%Queuesize

参考代码为《大话数据结构》
若文中有误,欢迎指出。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值