队列的顺序存储结构
队列的顺序存储结构
队列的顺序存储结构也就是说用的是数组来实现入队出队操作,但看似简单的问题却有值得思考的地方:
【方案一 】(出队时总是增加front的值)
这个方案的想法是,我们new一个数组,然后用一个指针base来保存其首元素的地址,用一个整数front来记录数组里面的首元素的序号,用一个rear来记录尾元素的下一个位置的序号,这样做的不好之处在于当我们入队元素时会在(rear+base)所指的地方存储入队元素,rear+1,出队时会让front+1这种简单的操作来实现出队,但这样做的不好之处就是我们所能存储的元素数量会越来越少,因为我们可以使用的只有front到数组结束的空间,front之前的都被浪费掉了。
这个方案的要求就是让队首元素始终固定在动态分配数组的第一个元素,当我们要执行入队操作时就在rear所指的地方添加,rear+1,但当我们要执行出队操作时就通过将第二个元素直至rear的所有元素前移一个单元来实现,可见此时空间没有浪费,但是当出队操作多且队列较长时就会很浪费时间,所以这也不是一种好的方案。
【最佳方案】
-------------------------------------------------------------------------------------------------------------------------------
空队列
----------------------------------------------------------------------------------------------------------------------------------------
上面给出了基础的思路,我们还是New一个动态数组,用base来保存返回的地址,front和rear以及queuesize都是int类型,front表示队首在这个数组中的序号,rear表示队尾元素下一个空白位置的序号,queuesize用来表示数组的大小,也就是动态数组一共有多少个元素。
上面右图中所示的就是空队列的情况,front==rear。 我们的构造函数就是要构造这样的一个状态,析构函数就是直接释放掉这个动态数组了delete [ ]base;
----------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------
上面的操作就是入队操作,可是调用之初的状态怎会如此呢,front竟然在rear的后面,因为是循环队列,也就不依rear front大小关系分前后了,其实可以把此时的状态折成一个环就会发现可以看成是 q1,q2,q3,rear,这种情况。
这个时候的确是满了,因为rear所指位置永远是空的,可是怎么判断满了呢,我们可以有这样一个观察结果:当队列已满(只有rear处为空即视为满了)时,折成环就会发现rear和front总是相邻的,譬如front为0,rear为3,这样也是满的,我们有这样一个公式来判断满没满:
注意上图中queuesize为4,满时只有两种情况,一为内部直接相邻,一为两端相邻,可见都符合上面的公,(3+1)%4==0成立。
满了就要重新分配空间,我们又new了一个数组,大小是前一个的两倍,然后复制过去。这个时候复制也要注意,一共要复制的元素个数是queuesize-1,我们可以使用for循环来控制,复制语句该怎么写呢?
*(newbase+1)=*( base+(front+i)%queuesize );
delete[]base;
base=newbase;
front=0;
rear=queuesize-1;//注意此时的queuesize还没有更新呢
queuesize*=2;
上面的语句完成了复制操作, base+(front+i)%queuesize这句写的的确很好。对于front大于rear的情况一取余就又回到前面去了。
rear=++rear%queuesize;
//要看%与++的使用情况了.如果a++%b,则%比++优先 如果++a%b,则++比%优先
上面第一句完成元素添加,第二句实现rear的进一步后移,似乎完美没必要除以queuesize取余操作啊,因为刚完成复制才添加一个元素空间大着呢!,但要注意:不是每次入队操作都恰逢队列已满,更多的时候,队列在一些的入队出队操作之后的状况是很难判断的,譬如rear指向了最后一个空间,front在不满足队列满的任何一个位置,这时rear++就会等于queuesize,但是呢,队列没满啊,必须再折返到前面的为0的位置,所以就有了一个先加一后取余的操作。
我们会发现:front往下直到rear这各区间内部存储了当前的所有元素(结成环状来看)rear到front都是空的。
------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------
上面就是出队操作了,出队操作相对于入队操作要简单不少,无非就是让(front+1)而已,但是又不是简单的+1就完事了:
return false;
e=*(base+front);//取出要出对的元素
front=(++front)%queuesize;//这一句就完成了出队操作,其实没必要加括号
return true;
上面几句就完成了出队操作。
那么怎么遍历呢?
2 while(i!=rear)
3 {
4 visit(base+i);//执行遍历操作
5 i=(i++)%queuesize;//这时括号就不该少了
6 }