1.队列的抽象类

1
2
3
4
5
6
7
8
9
10
template <class elemType>
class queue
{
public:
virtual bool isEmpty() const = 0;
virtual void enQueue(const elemType &x) = 0;
virtual elemType deQueue() = 0;
virtual elemType getHead() const = 0;
virtual ~queue() {}
};

2.顺序存储实现

实现分类

  1. 使用数组存储队列中的元素。
  2. 节点个数用MaxSize维护。
  3. 下标范围为0到MaxSize-1。
  4. 分三种组织方式:
    • 队头位置固定:出队会引起大量的数据移动。
    • 队头位置不固定:操作为O(1),但需要记录队头和队尾的位置,同时会浪费数组空间。
    • 循环队列:队头和队尾位置可以循环使用,避免了空间浪费,同时避免大量的数据移动。

3.循环队列

  • front指向队头前方的位置。
  • rear指向队尾位置。

操作实现

改进前:队头可以存储元素

1
2
rear = (rear + 1) % maxSize; elem[rear] = x;// 入队
front = (front + 1) % maxSize; // 出队
  • 最后一个元素出队时,front = (front + 1) % maxSize,front和rear相等,表示队列为空。
  • 只剩最后一个空位置,执行入队操作,rear = (rear + 1) % maxSize,front和rear相等,表示队列满。
    循环队列front除了初始状态时,其余时间不一定指向0号下标。Front和rear“你逃我追”

改进后:队头不存储元素

  • “牺牲”一个单元,规定front指向的单元不能存储队列元素,只起到标志作用,表示后面一个是队头元素。
  • 当rear“绕一卷”赶上front时,队列就满了。因此队列满的条件是:(rear + 1) % MaxSize == front.
  • 队列空的条件是:rear == front。即队头追上了队尾。

类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <class elemType>
class seqQueue: public queue<elemType>
{
private:
elemType *elem;
int front; // 队头位置
int rear; // 队尾位置
int maxSize;
void doubleSpace();
public:
seqQueue(int initSize = 10);
~seqQueue();
bool isEmpty() const;
void enQueue(const elemType &x);
elemType deQueue();
elemType getHead() const;
};

类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//构造函数
template <class elemType>
seqQueue<elemType>::seqQueue(int initSize)
{
elem = new elemType[initSize];
maxSize = initSize;
front = 0;
rear = 0;
}

//析构函数
template <class elemType>
seqQueue<elemType>::~seqQueue()
{
delete[] elem;
}

//入队
template <class elemType>
void seqQueue<elemType>::enQueue(const elemType &x)
{
if ((rear + 1) % maxSize == front) doubleSpace(); // 队列满时扩容
rear = (rear + 1) % maxSize; // 更新队尾位置
elem[rear] = x;
}

//扩充空间
template <class elemType>
void seqQuene<elemType>::doubleSpace()
{
elemType* tmp = elem;
elem = new elemType[2 * maxSize];
for (int i = 0; i < maxSize; ++i) {
elem[i] = tmp[(front + i) % maxSize]; // 重新排列元素
}
front = 0; // 重置队头位置
rear = maxSize - 1; // 更新队尾位置
maxSize *= 2; // 更新最大容量
delete [] tmp; // 释放旧空间
}

//出队
template <class elemType>
elemType seqQueue<elemType>::deQueue()
{
if (isEmpty()) throw "Queue is empty"; // 队列为空时抛出异常
front = (front + 1) % maxSize; // 更新队头位置
return elem[front]; // 返回队头元素
}

//判队空
template <class elemType>
bool seqQueue<elemType>::isEmpty() const
{
return front == rear; // 队头追上队尾,表示队列为空
}

4.队列的链表实现

对链式队列的操作基本等同于对链表的操作,只是需要维护队头和队尾指针,限制了修改链表的位置。此处不再实现。

顺序实现与链接实现的比较

  • 时间:两者都能在常量的时间O(1)内完成基本操作,但顺序队列由于采用回绕,使入队和出队的处理比较麻烦。
  • 空间:链接队列中每一个节点多一个指针字段,空间利用率较高。顺序队列由于长度预指定,数组中可能有大量未使用的空间。