一.顺序队列的改进
队列元素的出列是在队头,即下标为0的位置,那也就意味着,队列中的所有元素都得向前移动,以保证队列的队头(也就是下标为0的位置)不为空,此时的时间复杂度为0(n)。
可有时想想,为什么出队列时一定要全部移动呢,如果不去限制队列的元素必须存储在数组的前n个单元这一条件,出队的性能就会大大增加。也就是说,队头不需要一定在下标为0的位置,比如也可以是a[1]等。
而为了避免当只有一个元素时,队头和队尾重合使处理变得麻烦,引入两个指针,front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样当front等于rear时,此队列不是还剩一个元素,而是空队列。
对于队列最好的方法是使用链表实现,因为对于数组来说,队列可能会出现下面这种情况:
假设是长度为5的数组,初始状态,空队列如所示,front与 rear指针均指向下标为0的位置。然后入队a1、a2、a3、a4, front指针依然指向下标为0位置,而rear指针指向下标为4的位置。
出队a1、a2,则front指针指向下标为2的位置,rear不变,如下图所示,再入队a5,此时front指针不变,rear指针移动到数组之外。嗯?数组之外,那将是哪里?
问题还不止于此。假设这个队列的总个数不超过5个,但目前如果接着入队的话,因数组末尾元素已经占用,再向后加,就会产生数组越界的错误,可实际上,我们的队列在下标为0和1的地方还是空闲的。我们把这种现象叫做“假溢出”。
不可以继续添加元素,否则会造成数组越界而遭致程序出错。然而此时又不应该扩充数组,因为还有大量实际空间未被占用。
此时我们应该如何解决这个问题呢?我们将其实现为循环队列。
解决假溢出的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。
二.循环队列的引入
2.1总结来说,什么是循环队列?
由于在用数组实现队列的时候,队列有元素出列,front就向后移动,所以队列前面的空间就空了出来。当rear移动到LENGTH时,再入队会发生假溢出,也就是说实际上我们开辟的数组还有剩余空间,却因为rear越界表现为溢出,
为了更合理的利用空间,人们想了一个办法:将队列的首尾相连接。这样当rear移动到LENGTH时,会再从0开始循环。
那当什么时候队列满呢?当rear等于front的时候。可是队列为空的时候也是同样的条件,那不就没法判断了吗?
办法一是设置一个标志变量flag,当front == rear,且flag = 0时为队列空,当front == rear,且flag= 1时为队列满
办法二牺牲一个存储空间,front前面不存数据,当rear在front前面的时候就是满了(尾在头前就是满了)
我们主要讨论第二种方法
2.2理解循环队列
首先我们要说明的是循环队列仍然是基于数组实现的。但是为了形象化的说明问题,我们如下图所示
1.图中有两个指针front、rear,一个指示队头,一个指示队尾。
2.rear和front互相追赶着,这个追赶过程就是队列添加和删除的过程,如果rear追到head说明队列满了,如果front追到rear说明队列为空。
说明:
令队列空间中的一个单元闲置,使得队列非空时,Q.rear与Q.front之间至少间隔一个空闲单。
3.我们把它掰弯,用的是求余,这样两个值就不会跑出最大范围,并且可以实现弯曲的效果,所以说对于循环队列我们必须给定最大值MAXQSIZE。
这其实是我们臆想的,反正我们要做的就是利用循环来解决空间浪费的问题。
参考资料:
http://blog.csdn.net/zx249388847/article/details/51672129
https://www.cnblogs.com/chenliyang/p/6554141.html