队列
队列的逻辑结构
队列(queue)是只允许在一端进行插入操作,在另一端进行删除操作的线性表,允许插入(也称入队、进队)的一端称为队尾(queue-tail),允许删除(也称出队)的一端称为队头(queue-head)。
如图是一个有5个元素的队列,入队的顺序为a1,a2,a3,a4,a5,出队的顺序依然是a1,a2,a3,a4,a5,即最先入队者最先出队。所以队列中的元素除了具有线性关系外,还具有先进先出(first in first out)的特性。
顺序队列的存储结构
队列的顺序存储结构称为顺序队列(sequential)。假设队列有n个元素,顺序队列把所有元素存储在数组的前n个单元。如果把队头元素放在数组中下标为0的一端,则入队操作相当于追加,不需要移动元素,其时间性能为O(1);但出队操作的时间性能为O(n),因为要保证剩下的n-1个元素仍然存储在数组的前n-1个单元,所有元素都要向前移动一个位置。
如果放款队列的所有元素必须存储在数组的前n个单元这一条件,就可以得到一种更为有效的存储方法,如上图最后一个结构所示。此时入队和出队操作的时间性能都是O(1),因为没有移动任何元素,但是队头和队尾都是活动的,因此需要设置队头、队尾两个位置变量front和rear,入队时rear加1,出队时front加1.并且约定:front指向队头元素的前一个位置,rear指向队尾元素的位置。
循环队列的存储结构及实现
在顺序队列中,随着队列的插入和删除操作,整个队列向数组的高端移过去,从而产生了队列的“单向移动性”。当元素被插入到数组下标最大的位置之后,数组空间就用尽了,尽管此时数组的低端还有空闲空间,这种现象叫做假溢出(false overflow)。
解决假溢出的方法是将存储队列的数组看成是头尾相接的循环结构,即允许队列直接从数组中下标最大的位置延续到下标最小的位置。这可以通过取模操作来实现,设存储队列的数组长度为QueueSize,操作语句为rear = (rear + 1) % QueueSize。队列的这种头尾相接的顺序存储结构称为循环队列(circular queue)。
循环队列如何判定队空和队满
如下图(a)和图©所示,队列中只有一个元素,执行出队操作,则队头位置在循环意义下加1后与队尾位置相等,即队空的条件是front = rear。
如下图(a)和图©所示,数组中只有一个空闲单元,执行入队操作,则队尾位置在循环意义下加1后与队头位置相等,即队满的条件也是front = rear。
这样如何区分队空和队满的情况呢,可以浪费一个数组元素的空间,把图(a)和图©的情况视为队满,此时队尾位置和队头位置正好相差1,即队满的条件是 **(rear + 1) % QueueSize = front **
循环队列的实现
循环队列的类定义
const int QueueSize = 100;
template <typename DataType>
class CirQueue{
public:
CirQueue(); //构造函数,初始化空队列
~CirQueue(); //析构函数
void EnQueue(DataType x); //入队操作,将元素x入队
DataType DeQueue(); //出队操作,将元素x出队
DataType GetHead(); //取队头元素,并不删除
int Empty(); //判断队列是否为空
private:
DataType data[QueueSize]; //存放队列元素的数组
int front, rear; //游标,队头指针和队尾指针
};
成员函数的实现
template <typename DataType>
CirQueue<DataType>::CirQueue(){
//初始化一个空的循环队列只需将队头front和队尾rear同时指向数组的某一个位置,一般是数组的高端
rear = front = QueueSize - 1;
}
template <typename DataType>
void CirQueue<DataType>::EnQueue(DataType x){
if((rear + 1) % QueueSize == front)
throw "上溢";
rear = (rear + 1) % QueueSize;
data[rear] = x;
}
template <typename DataType>
DataType CirQueue<DataType>::DeQueue(){
if(rear == front)
throw "下溢";
front = (front + 1) % QueueSize;
return data[front];
}
template <typename DataType>
DataType CirQueue<DataType>::GetHead(){
if(rear == front)
throw "下溢";
return data[(front + 1) % QueueSize];
}
template <typename DataType>
int CirQueue<DataType>::Empty(){
return front == rear;
}
队列的连接存储结构及实现
队列的连接存储结构称为链队列(linked queue),通常用单链表表示,其结点结构与单链表的结点结构相同。为了使空队列和非空队列的操作一致,链队列也加上头结点。根据队列的先进先出特性,为了操作上的方便,设置队头指针指向链队列的头结点,队尾指针指向终端节点。
链队列的实现
链队列的类定义
template <typename DataType>
struct Node{
DataType data;
Node<DataType> *next;
};
template <typename DataType>
class LinkQueue{
public:
LinkQueue(); //初始化空的链队列
~LinkQueue(); //释放链队列的存储空间
void EnQueue(DataType x); //入队操作,将元素x入队
DataType DeQueue(); //出队操作,将队头元素出队
DataType GetHead(); //取链队列的队头元素
int Empty(); //判断链队列是否为空
private:
Node<DataType> *front, *rear; //队头和队尾指针
};
成员函数的实现
template <typename DataType>
LinkQueue<DataType>::LinkQueue(){
Node<DataType> *s = nullptr;
s = new Node<DataType>;
s->next = nullptr;
front = rear = nullptr;
}
template <typename DataType>
LinkQueue<DataType>::~LinkQueue(){
Node<DataType> *p;
while(front!=nullptr){
p = front;
front = front->next;
delete p;
}
}
template <typename DataType>
void LinkQueue<DataType>::EnQueue(DataType x){
Node<DataType> *s = nullptr;
s = new Node<DataType>;
s->data = x;
s->next = nullptr;
rear->next = s;
rear = s;
}
template <typename DataType>
DataType LinkQueue<DataType>::DeQueue(){
DataType x;
Node<DataType> *p = nullptr;
if(rear == front)
throw "下溢";
p = front->next;
x = p->data;
front->next = p->next;
if(p->next == nullptr)
rear = front;
delete p;
return x;
}
template <typename DataType>
DataType LinkQueue<DataType>::GetHead(){
DataType x;
Node<DataType> *p = nullptr;
if(rear == front)
throw "下溢";
p = front->next;
x = p->data;
return x;
}
template <typename DataType>
int LinkQueue<DataType>::Empty(){
return front == rear;
}