一、队列的定义
1.队列是一种特殊的线性表;
2.队列只能在线性表的两端进行操作:
队头(Front):取出数据元素的一段;
队尾(Rear):插入数据元素的一段。
二、队列的特性
由于队列只能在队头(Front)取数据,在队尾(Rear)插入数据,故队列的特性为:先进先出(First In First Out)。如下图所示:
(注:此图引用自狄泰软件学院唐老师ppt)
三、队列的操作
- 创建队列(Queue())
- 销毁队列(~Queue())
- 清空队列(clear())
- 进队列(add())
- 出队列(remove())
- 获取队列头元素(front())
- 获取队列的长度(length())
四、队列的实现
1.队列的抽象实现
Queue.h
#ifndef QUEUE_H
#define QUEUE_H
namespace Queue {
template <typename T>
class Queue
{
public:
virtual void add(const T& e) = 0;
virtual void remove() = 0;
virtual T front() = 0;
virtual void clear() = 0;
virtual int length() const = 0;
};
}
#endif // QUEUE_H
2.静态队列的实现
设计要点:
1.使用原生数组作为队列的存储空间;
2.队列的存储空间在创建队列的时候指定(由模板参数指定),且不能动态的改变。
3.为了提高队列的效率,采用循环计数方式:
关键操作:
进队列:m_space[m_rear] = e; m_rear = (m_rear + 1) % N;
出队列:m_front = (m_front + 1) % N;
队列状态:
队空:(m_length == 0) && (m_front == m_rear);
队满:(m_length == N) && (m_front == m_rear);
下面给出静态队列的实现:
#ifndef STATICQUEUE_H
#define STATICQUEUE_H
#include "Queue.h"
namespace Queue{
template <typename T, int N>
class StaticQueue : public Queue<T>
{
protected:
T m_space[N]; // 存储空间
int m_front; // 对头标识
int m_rear; // 队尾标识
int m_length; // 当前队列的长度
public:
StaticQueue()
{
m_front = 0;
m_rear = 0;
m_length = 0;
}
int capacity() const // O(1)
{
return N;
}
void add(const T& e) // O(1)
{
if (m_length < N) {
m_space[m_rear] = e;
m_rear = (m_rear + 1) % N; // 采用循环计数,以提高效率
m_length ++;
} else {
throw("No space in current queue ...");
}
}
void remove() // O(1)
{
if (m_length > 0) {
m_front = (m_front + 1) % N; // 采用循环计数,以提高效率
m_length --;
} else {
throw("No element in current queue ...");
}
}
T front() // O(1)
{
if (m_length > 0) {
return m_space[m_front];
} else {
throw("No element in current queue ...");
}
}
void clear() // O(1)
{
m_front = 0;
m_rear = 0;
m_length = 0;
}
int length() const // O(1)
{
return m_length;
}
};
}
#endif // STATICQUEUE_H
3.链式队列的实现
由于静态队列一次性创建指定大小的空间,如果队列的数据元素为类类型的话,就需要调用N次构造函数,故效率会比较低;另静态队列没法动态的改变队列的大小。
基于以上两点,链式队列便可以解决上述两个问题。
设计要点:
1.用类模板实现,是抽象父类Queue的直接子类;
2.在内部使用链式结构实现元素的存储;
3.为了提高队列操作的效率,内部采用双向循环链表存储数据。
4.只在链表的头部和尾部进行操作;
下面给出链式队列的实现:
#ifndef LINKQUEUE_H
#define LINKQUEUE_H
#include "Queue.h"
namespace Queue {
template <typename T>
class LinkQueue : public Queue<T>
{
protected:
struct Node
{
T value;
struct Node* next;
struct Node* prev;
};
// 为了防止泛指类型T在构造函数中抛出异常,导致队列初始化失败,故重新实现头结构
struct
{
char reserved[sizeof(T)];
struct Node* next;
struct Node* prev;
} m_header;
int m_length;
public:
LinkQueue()
{
m_length = 0;
m_header.next = reinterpret_cast<Node*>(&m_header);
m_header.prev = reinterpret_cast<Node*>(&m_header);
}
void add(const T& e) // O(1)
{
Node* node = new Node();
if (NULL != node) {
node->value = e;
Node* prev = m_header.prev;
/*将元素插入到链表的最后位置*/
m_header.prev = node;
node->prev = prev;
prev->next = node;
node->next = reinterpret_cast<Node*>(&m_header);
m_length ++;
} else {
throw("No memory to add element ...");
}
}
void remove() // O(1)
{
if (m_length > 0) {
Node* toDel = m_header.next;
Node* next = toDel->next;
/*删除第一个元素*/
m_header.next = next;
next->prev = reinterpret_cast<Node*>(&m_header);
m_length --;
delete toDel;
} else {
throw("No element in current queue ...");
}
}
T front() // O(1)
{
if (m_length > 0) {
return m_header.next->value;
} else {
throw("No element in current queue ...");
}
}
void clear() // O(n)
{
while (m_length > 0) {
remove();
}
}
int length() const // O(1)
{
return m_length;
}
~LinkQueue()
{
clear();
}
};
}
#endif // LINKQUEUE_H
测试:
main.cpp
#include <iostream>
#include <string>
#include "LinkQueue.h"
using namespace std;
using namespace Queue;
int main()
{
LinkQueue<int> queue;
for (int i=0; i<5; i++) {
queue.add(i);
}
while (queue.length() > 0) {
cout <<queue.front() << endl;
queue.remove();
}
return 0;
}
运行结果如下图所示:
由上图可以得出,最先进对列的数被先取出来,即先进先出,符合队列的特性。