1. 队列的基本概念
队列是一种先进先出(first in first out ,FIFO)的线性表,只允许在一端进行插入(入队)操作,在另一端进行删除(出队)操作。
在队列中,允许入队操作的一端称为队尾,允许出队操作的一端称为队头。设有队列
q=(a1,a2,⋯,an)
q
=
(
a
1
,
a
2
,
⋯
,
a
n
)
,则
a1
a
1
称为队头元素,
an
a
n
称为队尾元素,队列按
a1,a2,⋯,an
a
1
,
a
2
,
⋯
,
a
n
的顺序入队,按同样的顺序出队。队列的典型应用是操作系统中的作业排队,同时在离散事件模拟中一般都要使用队列。
2. 链队列
//.hpp文件
#pragma once
//节点类
template<typename elemType>
struct Node
{
//数据成员
elemType data;//数据域
Node<elemType>* next;//指针域
//构造函数
Node();//无参构造函数
Node(elemType item, Node<elemType>* Link = NULL);//已知数据域和指针域建立结构
};
//节点类的实现部分
template<typename elemType>
Node<elemType>::Node()
//操作结果:构造指针域为空的节点
{
this->next = NULL;
}
template<typename elemType>
Node<elemType>::Node(elemType item, Node<elemType>* Link)
//操作结果:构造数据域为item指针域为Link的节点
{
this->data = item;
this->next = Link;
}
//链队列表
template<typename elemType>
class LinkQueue
{
protected:
//链队列数据成员
Node<elemType>* front; Node<elemType>* rear;//队头队尾指针
//辅助函数
void Init();//初始化队列
public:
//抽象数据类型方法声明及重载编译系统默认方法声明
LinkQueue();//无参构造函数
virtual ~LinkQueue();//析构函数
int getLength() const;//获取队列的长度
bool isEmpty() const;//判断队列是否为空
void Clear();//将队列清空
void Traverse(void(*Visit)(elemType &e));//遍历队列
bool outQueue(elemType &e);//出队操作
bool getHead(elemType& e) const;//获取队头数据
bool InQueue(const elemType& e);//入队操作
LinkQueue(const LinkQueue<elemType>& copy);//复制构造函数
LinkQueue<elemType>& operator=(const LinkQueue<elemType>& copy);//重载赋值运算符
};
//链队列类的实现
template<typename elemType>
void LinkQueue<elemType>::Init()
{
rear = front = new Node < elemType > ;//生成头结点,对于头结点不存数据的初始化时,
//要new一个头结点出来,否则直接让头结点指向空指针
}
template<typename elemType>
LinkQueue<elemType>::LinkQueue()
{
Init();
}
template<typename elemType>
LinkQueue<elemType>::~LinkQueue()
{
Clear();
}
template<typename elemType>
int LinkQueue<elemType>::getLength() const
{
int count = 0;
for (Node<elemType>* curPosition = front->next; curPosition != NULL; curPosition = curPosition->next)
{
count++;
}
return count;
}
template<typename elemType>
bool LinkQueue<elemType>::isEmpty() const
{
return front == rear;
}
template<typename elemType>
void LinkQueue<elemType>::Clear()
//要学会这里清空链表的思想
{
elemType e;
while(front != NULL)
{
outQueue(e);
}
}
template<typename elemType>
void LinkQueue<elemType>::Traverse(void(*Visit)(elemType &e))
{
for (Node<elemType>* curPosition = front->next; curPosition != NULL; curPosition = curPosition->next)
{
(*Visit)(curPosition->data);
}
}
template<typename elemType>
bool LinkQueue<elemType>::outQueue(elemType &e)
{
if (isEmpty())
{
return false;
}
else
{
Node<elemType>* tempPtr = front->next;
e = tempPtr->data;
front->next = tempPtr->next;
if (rear == tempPtr)//要加这个判断,否则为空的时候rear != front
{
rear = front;
}
delete tempPtr;
return true;
}
}
template<typename elemType>
bool LinkQueue<elemType>::getHead(elemType& e) const
{
if (isEmpty())
{
return false;
}
else
{
e = front->next->data;
return true;
}
}
template<typename elemType>
bool LinkQueue<elemType>::InQueue(const elemType& e)
{
//这里要判断队列是否已满
Node<elemType>* tempPtr = new Node<elemType>(e);
if (tempPtr == NULL)
{
return false;
}
rear->next = tempPtr;
rear = tempPtr;
return true;
}
template<typename elemType>
LinkQueue<elemType>::LinkQueue(const LinkQueue<elemType>& copy)
{
if (copy.isEmpty())
{
Init();
}
else
{
Init();
for (Node<elemType>* tempPtr = copy.front->next; tempPtr != NULL; tempPtr = tempPtr->next)
{
InQueue(tempPtr->data);//注意这个技巧
}
}
}
template<typename elemType>
LinkQueue<elemType>& LinkQueue<elemType>::operator=(const LinkQueue<elemType>& copy)
{
if (© != this)
{
Clear();
Init();
for (Node<elemType>* tempPtr = copy.front->next; tempPtr != NULL; tempPtr = tempPtr->next)
{
InQueue(tempPtr->data);//注意这个技巧
}
}
return *this;
}
//测试文件
#include <iostream>
#include "LinkQueue.h"
using namespace std;
template<typename elemType>
void Visit(elemType &e)
{
cout << e << endl;
e = e + 1;
}
int main(int argc, char* argv[])
{
LinkQueue<int> myLinkQueue;
myLinkQueue.InQueue(1);
myLinkQueue.InQueue(2);
myLinkQueue.InQueue(3);
myLinkQueue.Traverse(Visit);
int e;
myLinkQueue.outQueue(e);
cout << e << endl;
getchar();
return 0;
}
3 循环队列——队列的顺序存储结构
顺序存储结构的队列是存储在一个一维数组中的,因此它会出现假溢出的问题。下面先简单说明一下什么是假溢出:
在上图的(d)中,将不可以再继续插入新元素了否则会因数组越界而出错,显然此时的实际可以空间还没有用完,这种情形之下在插入一个新的元素产生的溢出称为“假溢出”。
那么这个问题该如何解决呢?为了解决这个问题,我们将顺序队从逻辑上看成一个环,成为循环队列。当队头(front)和队尾(rear)进入到 maxSize - 1时,再进一位就自动的移动到0,可用取余运算符(%)简单实现。
队头进1:front = (front + 1)%maxSize
队尾进1:rear = (rear + 1)%maxSize
上图是循环队列的逻辑结构,从图可以知道,在对空和队满时都有:
front == rear
因此从front == rear无法判断是队空和队满的状态,因此我们通过下面的方式来判断:
(1) 另设一个标识符区别队列是对空还是队满
(2) 少用一个元素空间,约定队头在队尾指针的下一位置(指环状的下一个位置)时最为队满的标志。接下来的实现采用这种方式
//.hpp文件
#pragma once
#define DEFAULT_SIZE 10
//循环队列类
template<typename elemType>
class Squeue
{
protected:
//成员变量
int front, rear;//队头队尾
int maxSize;//队列的最大元素的个数
elemType* elems;//元素存储结构
//辅助函数
bool Full() const;//判断队列是否为空
void Init();//初始化队列
public:
//方法
Squeue(int size = DEFAULT_SIZE);//构造函数
virtual ~Squeue();//析构函数
int getLength() const;//获取队列长度
bool isEmpty() const;//判断队列是否为空
void Clear();//清空队列
void Traverse(void(*Visit)(elemType& e));//遍历队列
bool OutQueue(elemType& e);//出队操作
bool getHead(elemType& e) const;//取队头操作
bool InQueue(const elemType& e);//入队操作
Squeue(const Squeue<elemType>& copy);//复制构造函数
Squeue<elemType>& operator=(const Squeue<elemType>& copy);//重载复制运算符
};
//循环队列类的实现
template<typename elemType>
bool Squeue<elemType>::Full() const
{
return getLength() == maxSize - 1;
}
template<typename elemType>
void Squeue<elemType>::Init()
{
rear = front = 0;
}
template<typename elemType>
Squeue<elemType>::Squeue(int size)
{
maxSize = size;
elems = new elemType[maxSize];
Init();
}
template<typename elemType>
Squeue<elemType>::~Squeue()
{
delete[] elems;
}
template<typename elemType>
int Squeue<elemType>::getLength() const
{
return (rear - front + maxSize) % maxSize;//这个要注意
}
template<typename elemType>
bool Squeue<elemType>::isEmpty() const
{
return rear == front;
}
template<typename elemType>
void Squeue<elemType>::Clear()
{
rear = front = 0;
}
template<typename elemType>
void Squeue<elemType>::Traverse(void(*Visit)(elemType& e))
//注意点:如何判断队列结束,以及指针的更新
{
for (int curPosition = front; curPosition != rear; curPosition = (curPosition + 1) % maxSize)//这里求长度以及加运算后都要求余
{
(*Visit)(elems[curPosition]);
}
}
template<typename elemType>
bool Squeue<elemType>::OutQueue(elemType& e)
{
if (isEmpty())
{
return false;
}
else
{
e = elems[front];
front = (front + 1) % maxSize;
return true;
}
}
template<typename elemType>
bool Squeue<elemType>::getHead(elemType& e) const
{
if (isEmpty())
{
return false
}
else
{
e = elems[front];
return true;
}
}
template<typename elemType>
bool Squeue<elemType>::InQueue(const elemType& e)
{
if (!Full())
{
elems[rear] = e;
rear = (rear + 1) % maxSize;
return true;
}
else
{
return false;
}
}
template<typename elemType>
Squeue<elemType>::Squeue(const Squeue<elemType>& copy)
{
elems = new elemType[copy.maxSize];
maxSize = copy.maxSize;
front = copy.front;
rear = copy.rear;
for (int curposition = front; curposition != rear; curposition = (curposition + 1) % maxSize)
{
elems[curposition] = copy.elems[curposition];
}
}
template<typename elemType>
Squeue<elemType>& Squeue<elemType>::operator=(const Squeue<elemType>& copy)
{
if (© != this)
{
delete[] elems;
elems = new elemType[copy.maxSize];
maxSize = copy.maxSize;
front = copy.front;
rear = copy.rear;
for (int curposition = front; curposition != rear; curposition = (curposition + 1) % maxSize)
{
elems[curposition] = copy.elems[curposition];
}
}
return *this;
}
//测试文件
#include <iostream>
#include "Squeue.hpp"
using namespace std;
template<typename elemType>
void Visit(elemType &e)
{
cout << e << endl;
e = e + 1;
}
int main(int argc, char* argv[])
{
Squeue<int> myqueue;
myqueue.InQueue(1);
myqueue.InQueue(2);
myqueue.InQueue(3);
myqueue.InQueue(4);
myqueue.Traverse(Visit);
int e;
myqueue.OutQueue(e);
cout << e << endl;
Squeue<int> myqueue1;
myqueue1 = myqueue;
myqueue1.Traverse(Visit);
myqueue1.Clear();
myqueue1.Traverse(Visit);
getchar();
return 0;
}
4. 优先队列
上面介绍的队列过于死板,对于一些特殊的情况我们不能使用先进先出的策略。比如,在医院中,对于病危的病人,可能他们是后来的,但是应该优先给他们救治。因此,优先队列就产生了。
优先队列是一种数据结构,优先队列有两种数据类型:最小优先队列和最大优先队列。最小优先队列的出队操作OutQueue()将删除最下的数据元素。最大优先队列是删除最大的。
优先队列有多种实现方式。一种简单的方式是在入队时就对队列中的数据元素进行排列,使其顺序合理化。当然我们也可以在出队时选择合适的数据元素将其删除。这里选择第一中方式。
4.1 最小优先链队列
//最小优先队列类
template<typename elemType>
class MinPriorityLinkQueue :public LinkQueue<elemType>
{
public:
//重载入队操作说明
bool InQueue(const elemType& e);
};
//最小优先链队列的实现部分
template<typename elemType>
bool MinPriorityLinkQueue<elemType>::InQueue(const elemType& e)
{
Node<elemType>* curPtr = LinkQueue<elemType>::front->next;//指向当前节点
Node<elemType>* curPrePtr = LinkQueue<elemType>::front;//指向当前节点的前驱节点
while (curPtr!= NULL && curPtr->data <= e)
{
curPrePtr = curPtr;
curPtr = curPtr->next;
}
Node<elemType>* tempPtr = new Node<elemType>(e, curPtr);
if (tempPtr == NULL)
{
return false;
}
curPrePtr->next = tempPtr;
if (curPrePtr == LinkQueue<elemType>::rear)
{
LinkQueue<elemType>::rear = tempPtr;//指向新队尾
}
return true;
}
4.2 最大优先链队列
//最大优先链队列
template<typename elemType>
class MaxPriorityLinkQueue :public LinkQueue < elemType >
{
public:
//重载入队操作
bool InQueue(const elemType& e);
};
//最大优先链队列实现部分
template<typename elemType>
bool MaxPriorityLinkQueue<elemType>::InQueue(const elemType& e)
{
Node<elemType>* curPtr = LinkQueue<elemType>::front->next;//指向当前节点
Node<elemType>* curPrePtr = LinkQueue<elemType>::front;//指向当前节点的前驱节点
while (curPtr != NULL && curPtr->data >= e)
{
curPrePtr = curPtr;
curPtr = curPtr->next;
}
Node<elemType>* tempPtr = new Node<elemType>(e, curPtr);
if (tempPtr == NULL)
{
return false;
}
curPrePtr->next = tempPtr;
if (curPrePtr == LinkQueue<elemType>::rear)
{
LinkQueue<elemType>::rear = tempPtr;//指向新队尾
}
return true;
}
4.3 最小优先循环队列
//最小优先循环队列类
template<typename elemType>
class MinPrioritySquue : public Squeue < elemType >
{
public:
//重载入队操作声明
bool InQueue(const elemType& e);
};
//最小优先循环队列实现部分
template<typename elemType>
bool MinPrioritySquue<elemType>::InQueue(const elemType& e)
{
if (Squeue<elemType>::Full())
{
return false;
}
else
{
int curPosition = Squeue<elemType>::front;
while(curPosition != rear && Squeue<elemType>::elems[curPosition] <= e)
{//将所有大于e的数据元素向前一个
Squeue<elemType>::elems[(curPosition - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize] =
Squeue<elemType>::elems[curPosition];
curPosition = (curPosition + 1) % Squeue<elemType>::maxSize;
}
//curPosition的前一个位置为e的插入位置
Squeue<elemType>::elems[(curPosition - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize] = e;
//将front前移一个位置
Squeue<elemType>::front = (Squeue<elemType>::front - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize;
return true;
}
}
4.4 最大优先循环队列
//最大优先循环队列类
template<typename elemType>
class MaxPrioritySquue : public Squeue < elemType >
{
public:
//重载入队操作声明
bool InQueue(const elemType& e);
};
//最大优先循环队列实现部分
template<typename elemType>
bool MaxPrioritySquue<elemType>::InQueue(const elemType& e)
{
if (Squeue<elemType>::Full())
{
return false;
}
else
{
int curPosition = Squeue<elemType>::front;
while (curPosition != rear && Squeue<elemType>::elems[curPosition] >= e)
{//将所有大于e的数据元素向前一个
Squeue<elemType>::elems[(curPosition - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize] =
Squeue<elemType>::elems[curPosition];
curPosition = (curPosition + 1) % Squeue<elemType>::maxSize;
}
//curPosition的前一个位置为e的插入位置
Squeue<elemType>::elems[(curPosition - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize] = e;
//将front前移一个位置
Squeue<elemType>::front = (Squeue<elemType>::front - 1 + Squeue<elemType>::maxSize) % Squeue<elemType>::maxSize;
return true;
}
}