一、队列的实现有两种方式
顺序队列
链式队列
由于链式队列需要用到链表模板,因此,在代码实现链表后再实现链式队列
顺序队列实际上是由 数组 来承担主要的存储任务。每在队尾放进一个新元素后,rear向后移动一位,每从队首提取一个元素之后,front会向后移动一位。因此,不可避免的会遇到数组的长度不够用的问题。
这里的解决方法是,将线性数组围成一个环,从而实现空间的重复利用。
这里将数组连成环有一个很重要的思想——取余思想
这个思想的精妙之处在于,他没有用物理方式像链表一样把尾指针指向头节点,而是创造性的将这个 “把直棍掰弯” 的物理问题转化成为了一个数学计算问题。
注意下面的代码中,每次向后移动rear时都会用到以下代码。
rear = (rear + 1) % maxSize;
这使得rear即使到了数组尾部的索引时,取余%操作会将他重新放到数组的头部进行存储。
这个思想还可以用到同样类似的问题中,通过简单的数学计算达成目的。
二、注意点:判断队列是否满
另外,在这个代码中还有一个重点
如何判断队列是否是满的
因为引入了将数组转化为循环结构,因此判断队列是否满不能直接用 rear == maxsize 来判断,因为有可能数组的前端位置还有空间。
于是,我们用以下代码来判断队列是否满
(rear + 2) % maxSize) == front
这里 rear +2有两个原因
1.数组是从0开始索引的,因此要+1
2.我们为队列申请的存储空间比原本的值要大1,因此再+1
取余之后判断是否和front相等,这里只有一种情况相等,就是当数组满的时候。
三、注意点子类的length函数被设置为虚函数
virtual int length() const { //length 函数,返回队列实际使用长度
return ((rear + maxSize) - front + 1) % maxSize;
}
这是子类中的length函数,Shaffer将他声明成了虚函数。
这里关于虚函数,有一点理解:
· 如果父类是虚函数(不是纯虚函数),子类是实函数,那么 父类->该方法,则调用的是子类的函数
如果父类是 实函数,子类是 实函数,那么 父类->该方法 直接调用父类函数
总结:如果该方法是virtual ,那么他会向子类中找非virtual 的函数去调用
例:
#include <iostream>
class x {
public:
virtual void print() {
std:: cout << "x" << std:: endl;
}
};
class y: public x {
public:
void print() {
std::cout << "y" << std:: endl;
}
};
int main() {
x *X = new y;
X->print();
return 0;
}
在上述代码中,父类的print方法是虚函数,子类的是实函数,我们执行父类的print,输出结果为
y
class x {
public:
void print() {
std:: cout << "x" << std:: endl;
}
};
但我们将父类的print方法改为实函数,输出结果为
x
由此可见
程序会先向子类中找非virtual方法调用,如果没有,则调用此类的方法
如果父子都为virtual,那么实际是哪类,就用哪类的方法
四、队列的代码实现
Queue.h文件
#pragma once
#include<bits/stdc++.h>
//断言函数,如果不符合条件a,则打印字符串b
template<typename E>class Queue
{
private:
void operator =(const Queue&) {};
Queue(const Queue&) {};
public:
Queue() {}; //类模板的成员函数通常定义在头文件中
virtual ~Queue() {}; //析构函数
virtual void Assert(bool a, std::string b);//断言函数 如果不符合条件a,则打印字符串b
virtual void clear() = 0; //重新初始化队列,用户负责回收队列使用的储存空间
virtual void enqueue(const E&) = 0; //将一个元素进行如队列操作,放进队列的尾部
virtual E dequeue() = 0; //返回并删除队头的元素
virtual const E& frontValue() = 0;//取队头的元素但是不删除,第二个const表示不修改所属对象的状态
virtual int length() const = 0; //返回队列的长度
};
template<typename E>class QueueA: public Queue<E>//顺序队列的实现AQueue
{
private:
int maxSize; //队列的最大空间
int front; //队列头部元素的索引
int rear; //队列尾部元素索引
E* listArrey; //存储队列元素的数组
public:
QueueA(int size = 10) { //构造函数,默认大小为10
maxSize = size + 1; //最大空间比定义的多1
rear = 0; //让队尾的索引指向队尾
front = 1; //对首的索引指向队首
listArrey = new E[maxSize]; //初始化存储空间
}
~QueueA() { //析构函数
delete[] listArrey;
}
void clear() { rear = 0; front = 1; }//重置队列
void enqueue(const E& it); //将#it放进队列尾部
virtual int length() const { //length 函数,返回队列实际使用长度
return ((rear + maxSize) - front + 1) % maxSize;
}
E dequeue(); //返回并删除队首元素
const E& frontValue(); //提取队首元素但是不删除
void print();//打印队列中的元素
};
template<typename E>
inline void QueueA<E>::enqueue(const E& it)
{
Queue<E>::Assert(((rear + 2) % maxSize) != front, "Queue is full");
//当前面的条件式((rear + 2) % maxSize)== front时,队列是满的
rear = (rear + 1) % maxSize; //rear向后移动一位,这里考虑了循环队列的情况,即如果rear在数组的末尾,则从头开始存储
listArrey[rear] = it; //将it值存在队尾的位置
}
template<typename E>
inline E QueueA<E>::dequeue()
{
Queue<E>::Assert(length() != 0, "Queue is empty");//如果函数的长度为0
E it = listArrey[front]; //读取队首元素
front = (front + 1) % maxSize; //队首索引向后移动一位
return it;
}
template<typename E>
inline const E& QueueA<E>::frontValue()
{
Queue<E>::Assert(length() != 0, "Queue is empty");
return listArrey[front];
}
template<typename E>
inline void QueueA<E>::print()
{
Queue<E>::Assert(length() != 0, "Queue is empty");
for (int i = front; i <= rear; i++)
{
std::cout << listArrey[i] << " ";
}
std::cout << std::endl;
}
template<typename E>
inline void Queue<E>::Assert(bool a, std::string b)
{
if (!a)
{
std::cout << b << std::endl;
}
}
测试文件
test.cpp
#include "Queue.h"
int main() {
QueueA<int> a(20); //初始化一个20长度的队列
a.enqueue(1);
a.enqueue(2);
a.enqueue(3);
a.enqueue(4);
a.enqueue(5);
a.enqueue(6);
a.enqueue(7);
a.enqueue(8);
a.enqueue(9);
a.enqueue(10);
a.enqueue(11);
a.print();
std::cout << a.frontValue() << std::endl; //测试frontvalue函数
a.print();
std::cout << a.dequeue() << std::endl; //测试dequeue函数
a.print();
a.clear(); //测试clear函数
a.print(); //若clear成功,则控制台应该输出断言函数的字符串“Queue is empty”
return 0;
}
运行结果