循环队列

♀尐吖头ヾ 提交于 2020-01-19 21:51:50

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

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

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

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

空队列

0 1 2 3 4 5
front rear
↓ ↓

只有一个元素:

0 1 2 3 4 5
front rear
a1

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

0 1 2 3 4 5
front rear
a3 a4 a5 a6

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

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

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

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

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

0 1 2 3 4 5
front rear
a1 a2 a3 a4 a5

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

0 1 2 3 4 5
rear front
a7 a3 a4 a5 a6

所以为了综合以上两种情况,判断队列满的条件为:
(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:

0 1 2 3 4 5
rear front
a7 a3 a4 a5 a6

一段是 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

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!