编程基础 - 队列 (Queue)
本文意指简明扼要的描述队列,并用C++从零到有的实现栈。
需要一定的数据结构与程序语言的基础,尤其是要了解什么是顺序表。
文章目录
1 队列的简介(Introduction of Queue)
-
定义:队列是一种只允许在一端插入,另一端删除的特殊线性表
- 允许插入的一端称为后端(Back/Rear)或队尾(Tail)
- 允许删除的一端称为前端(Front)或队头(Head)
- 插入操作称为进队或入队(Enqueue)
- 删除操作称为退队或出队(Dequeue)
-
限制:只允许在队头删除,队尾插入
-
运算规则(特点):先进先出(First-In-First-Out,FIFO)或后进后出(Last-In-Last-out,LILO)
2 队列的主要方法(Main Methods of Queue)
队列的主要操作包括:
-
初始化(Initialize)
-
入队(Enqueue):插入元素;
-
出队(Dequeue):如果队列中有元素,删除队头元素;
-
获取队头元素(GetFront):如果队列不为空,获取队头元素;
-
清空队列(Clear):如果队列不为空,则删除所有元素;
-
判断队列空(IsEmpty):栈空返回
true
,否则返回false
;
3 队列的实现(C++ Code)
虽然在各种高级语言中,队列已经都被实现了:
-
在C++中,使用栈需要加入
#include <queue>
。 -
在C#中,使用栈需要加入
using System.Collection.Generic;
。
但在这里为了更好的理解它,我们将用C++自己实现栈。
提示:以下代码编译器为VC++,属性__declspec(property...)
代码在其它编译器可能不通用,如果用其它编译器可删除这一行,直接使用方法代替。
3.1 队列的抽象类(Abstract Class)
首先,我们新建一个头文件起名为Queues.h
。它将包含如下内容:
-
一些需要的常量(主要是顺序结构需要):
- 队列的默认数组长度
- 对立的默认数组长度增长率
-
一个队列的模板抽象类:包含了队列的主要方法的虚函数。
-
一些需要的包含库(
#include
)
我们同时为这些内容放入 命名空间Queues
(之后所有代码都在它之中) 中:
#pragma once
#include <stdexcept> // Build-in exceptions
namespace Queues
{
constexpr int DEFAULT_MAX_COUNT = 255; // Max Count of Sequential Structure
constexpr int DEFAULT_INCREAMENT = 16; // Increament of Sequential Structure
template<typename T>
class AbstractQueue
{
public: // Constructor
virtual ~AbstractQueue()
{
};
public: // public Properties
inline virtual int GetCount() = 0;
__declspec(property(get = GetCount)) int count;
public: // public Methods
virtual void Enqueue(T item) = 0;
virtual T Dequeue() = 0;
virtual T Front() = 0;
virtual T Peek();
virtual void Clear() = 0;
virtual bool Empty() = 0;
};
template<typename T>
T AbstractQueue<T>::Peek()
{
return Front();
}
}
-
#include <stdexcept>
:程序异常库,里面有许多常用的异常错误信息 -
DEFAULT_MAX_COUNT
:数组队列的默认长度; -
DEFAULT_INCREAMENT
:数组队列的默认增长长度; -
template<typename T> class AbstractQueue
:对立的模板抽象类(T
为元素的类型)int GetCount()
与int count
:是一个属性方法,获取队列内元素个数void Enqueue(T item)
:入队T Dequeue()
:出栈T Front()
:获取队头元素void Clear()
:清空队列bool Empty()
:队列是否为空
Tips
constexpr int
可以替换成#define
:
#define DEFAULT_MAX_COUNT 255
#define DEFAULT_INCREAMENT 16
有了基类之后,我们来完成一些常见的队列形式。
3.2 数组型队列(Queue Using Array)
在数组存储方式中,数组是存储数据的主要类型。而我们在进行队列操作时,需要两个变量分别指向队头与队尾,从而数组型结构需要的变量:
-
元素数组:存储元素
- 数组初始化的长度
- 当数组满时,需要增长的长度
-
队头下标:指向队头元素(出队位置)
-
队尾下标:指向下一个插入位置
而在方法上,除了继承下来的方法,我们需要增加一些顺序结构的必要方法:
-
队尾到达数组最大值的动作:
- 当队列满时,扩充队列;
- 当队列不满时,整体移动元素到从数组起始位置开始
-
队列是否满
所以,我们建立一个新的头文件SequentialQueue.h
来写顺序结构,并将顺序栈命名为SequentialQueue
,
即,我们的最终结构为:
#pragma once
#include "Queues.h"
namespace Queues
{
template<typename T>
class SequentialQueue : virtual public AbstractQueue<T>
{
public: // Constructor
SequentialQueue();
virtual ~SequentialQueue() override;
protected: // protected Fields
T* m_Items; // 元素数组
int m_FrontIndex; // 队头下标
int m_RearIndex; // 队尾下标
int m_MaxCount; // 元素数组长度
int m_Increament; // 元素数组增长长度
public: // public Properties
inline virtual int GetCount() override;
inline virtual int GetMaxCount();
inline int GetIncreament();
inline void PutIncreament(int value);
__declspec(property(get = GetCount)) int count;
__declspec(property(get = GetMaxCount)) int maxCount;
__declspec(property(get = GetIncreament, put = PutIncreament)) int increament;
protected: // protected Methods
// 扩充队列。
// 如果队列满了,或余量 < 增长率,则每次扩充`m_Increament`的长度。
// 如果队列不满,且余量 >= 增长率,则将元素重置成从0开始
virtual void Overflow();
public: // public Methods
virtual void Enqueue(T item) override;
virtual T Dequeue() override;
virtual T Front() override;
virtual void Clear() override;
virtual bool Empty() override;
virtual bool Full(); // 队列是否满
};
}
3.2.1 初始化与销毁(Initialize and Destroy)
在构造函数,我们主要是进行数组的初始化:
template<typename T>
inline SequentialQueue<T>::SequentialQueue()
{
m_FrontIndex = 0;
m_RearIndex = 0;
m_MaxCount = DEFAULT_MAX_COUNT;
m_Increament = DEFAULT_INCREAMENT;
m_Items = new T[m_MaxCount];
if (m_Items == 0)
{
throw std::bad_alloc(); // 分配内存失败
}
}
类似的,在析构函数中,我们主要是对数组的销毁:
template<typename T>
inline SequentialQueue<T>::~SequentialQueue()
{
if (m_Items != 0)
{
delete[] m_Items;
}
m_FrontIndex = 0;
m_RearIndex = 0;
m_MaxCount = 0;
m_Increament = 0;
}
<