本系列文章主要学习“队列”这种数据结构的基本操作、实现方式、应用和扩展(双端队列)。其中,本文将介绍队列这种数据结构的特点和一种实现方式。
一、Queue(点击打开链接)
前面学习的Stack是在“pile”的同一端添加或删除元素,是一种FILO型的线性表;而Queue是在“pile”的一端添加元素,在“pile”的另一端删除元素,是一种FIFO型的线性表,如下图。
队列的基本操作就是“入队”和“出队”。基础类接口如下:
<span style="font-size:14px;">#pragma once
#include "Container.h"
class Queue : public virtual Container
{
public:
virtual Object & Head() const = 0;
virtual void Enqueue(Object &) = 0;
virtual Object & Dequeue() = 0;
};</span>
二、QueueAsArray
同Stack一样,Queue既可以用动态数组实现,也可以用链表实现。先看它的数组实现:
<span style="font-size:14px;">#pragma once
#include "Queue.h"
#include "DynamicArray.h"
using namespace FoundationalDataStructure;
class QueueAsArray : public virtual Queue
{
public:
QueueAsArray(unsigned int);
~QueueAsArray();
void Purge();
void Accept(Visitor &) const;
Object & Head() const ;
void Enqueue(Object &) ;
Object & Dequeue() ;
protected:
int CompareTo(Object const &) const;
protected:
Array<Object*> array;
unsigned int head;
unsigned int tail;
};
</span>
用数组来实现的队列,它包含了三个成员:head、tail和array。其中array就是存放元素指针的数组,而head和tail是数组元素的两个跟踪index。事实上,还有一个成员继承自“Container”,即“count”,元素计数。
关于这三个int型成员的关系,如下:
图a表示,一般情况下,head表示队列最左端元素的下标,tail表示队列最右端元素的下标。“count = tail - head + 1”。
图b演示某些情况下,tail和head都增长,出现交叉(生成者 vs. 消费者)
图c表示只有一个数组元素的情况,“head = tail”,注意元素的实际位置可以在数组的任一位置。
图d展示了一种判断空队列的方法,“head越过了tail”。
<span style="font-size:14px;">#include "stdafx.h"
#include "QueueAsArray.h"
QueueAsArray::QueueAsArray(unsigned int size)
: array(size)
, head(0)
, tail(size - 1)
{
}
QueueAsArray::~QueueAsArray()
{
Purge();
}
void QueueAsArray::Purge()
{
if (IsOwner())
{
for (unsigned int i = 0; i < count; ++i)
{
delete array[head];
if (++head == array.Length())
head = 0;
}
}
count = 0;
}
void QueueAsArray::Accept(Visitor & visitor) const
{
for (unsigned int i = 0; i < count && !visitor.IsDone(); ++i)
visitor.Visit(*array[i]);
}
Object & QueueAsArray::Head() const
{
if (count == 0)
throw std::domain_error("queue is empty");
return *array[head];
}
void QueueAsArray::Enqueue(Object & object)
{
if (count == array.Length())
throw std::domain_error("queue is full");
if (++tail == array.Length())
tail = 0;
array[tail] = &object;
++count;
}
Object & QueueAsArray::Dequeue()
{
if (count == 0)
throw std::domain_error("queue is empty");
Object &result = *array[head];
if (++head == array.Length())
head = 0;
--count;
return result;
}
int QueueAsArray::CompareTo(Object const & object) const
{
return -1;
}
</span>
注:
1)这是一个固定长度的队列,队列的size即数组的size。观察Enqueue和Dequeue的实现代码,它们分别对tail和head与数组长度进行比较,如果相等,则清零。
2)head和tail只进行++运算。这样,也就很容易出现图b所示的情况。故,对队列来说左右两端是不交叉的,而它的实现数组是可以交叉的。
3)Stack只有一个成员,即count,它可以++,也可以--运算。