顺序队列
有效地实现顺序队列有些棘手,因为如果只是对顺序表的实现进行简单转换,效率不会很高。
假设队列有n个元素,实现顺序表需要把所有元素都存储在数组的前n个位置上。如果选择队列的尾部元素放在位置0,则dequeue操作的时间代价仅为θ(1),因为队列最前面的一个元素(要被删除的元素)是数组最后面的一个元素(处于位置n - 1)。但是enqueue操作的时间代价为θ(n),因为必须把队列中当前n个元素的在数组中向后移动一个位置。如果反过来,把队列的尾部元素放在位置n - 1,则enqueue操作相当于线性表的append操作,时间代价仅为θ(1),但此时的dequeue操作的时间代价就为θ(n)了。
如果放宽队列的所有元素必须出于数组的前n个位置这一条件,就可以得到一种更有效的实现方法。仍然保证队列的元素存储在连续的数组位置中,但是队列的内容允许在数组中移动,如下图所示。此时enqueue操作和dequeue操作的时间代价均为θ(1),因为队列中没有任何需要移动的元素。
经过多次使用后,顺序队列的元素将移动到数组的后面。上图为一开始插入初始四个
数20,5,12和17后的队列;下图为先删除了20和5,然后插入3和4的队列
但是这种实现方法有一个新的问题。假设初始时队列的头在位置0,新元素连续插入到数组中编号较高的位置上。当从队列中删除元素时,front的值增加。随着时间的推移,整个队列向数组中标号较高的位置移去。尽管此时数组的低端还可能有空闲的位置,即先前从队列中删除的元素所占的位置,一旦一个元素插入到数组中编号最高的位置上之后,队列的空间就用尽了。
为了解决上述问题,我们可以通过假定数组是循环的来解决,即允许队列直接从数组中编号最高的位置延续到编号最低的位置。使用取模操作可以很容易实现。在这种方法中,数组中位置的编号为0到size - 1, size - 1被定义为位置0(等价于位置size % size) 的前趋。下图说明了这种方法。
对于这种实现方式还有一个虽然小却十分重要的问题,就是如何判断队列是空还是满。因为根据上图可知,假设front存储队列中队首元素的位置号,rear存储队尾元素的位置号。当队列为满时front在rear的后面,当队列为空时front也在rear的后面,因为当队列中只有一个元素时,front和rear指向同一个元素。为了解决这个问题,我们可以设置数组的大小为n + 1,但是只存储n个元素,这样的话当队列为满时,rear的后一个位置是空数组空间,再后一个位置是front;队列为空时,rear的后一个位置就是front。
顺序队列的实现如下:
template <class Elem>
class AQueue : public Queue<Elem>
{
private:
int size; // 当前队列的大小
int front; // 队首的位置
int rear; // 队尾的位置;
Elem *listArray; // 用来存储队列中的元素的数组
enum { DefaultListSize = 50 }; // 默认的队列最大范围
public:
AQueue(int sz = DefaultListSize)
{
size = sz + 1; // 创建的数组大小比需求的多一个
rear = 0;
front = 1;
listArray = new Elem[size];
}
~AQueue() { delete[] listArray; }
// 清空队列的元素
void clear() { front = 1; rear = 0; }
// 入队
bool enqueue(const Elem& elem)
{
// 判断队列是否已满,rear后面的第二个元素是否为front
if (((rear + 2) % size) == front) return false;
rear = (rear + 1) % size;
listArray[rear] = elem;
return true;
}
// 出队
bool dequeue(Elem& elem)
{
//判断对视是否为空
if (length() == 0) return false;
elem = listArray[front];
front = (front + 1) % size;
return true;
}
// 返回队首元素
bool frontValue(Elem& elem) const
{
//判断对视是否为空
if (length() == 0) return false;
elem = listArray[front];
return true;
}
// 返回队列长度
int length() const
{
return ((rear + size)- front + 1) % size;
}
};