以下说法都是针对顺序存储结构实现的队列
先理清思路
(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
参考代码为《大话数据结构》
若文中有误,欢迎指出。
来源:CSDN
作者:Java磕起来
链接:https://blog.csdn.net/Unknownfuture/article/details/104043795