数据结构循环队列C++实现

1.队列的概念

队列只允许在表的一端插入,另一端删除。允许插入的一端叫做队尾,允许删除的一端叫做对首。队列的特性叫“先进先出”。和栈一样,队列的存储形式也有两种,基于数组的存储表示和基于链表的存储表示。本文先实现基于数组的存储队列,也叫顺序队列。在顺序队列中设置两个指针,front和rear,front指示队头的位置,rear指示队尾的位置(说是指针,实际仍不是c语言的指针*,而是类似下标或索引的作用)。如下图所示:

  1. 当队列为空时,front=rear=0;
  2. 有新元素入队时,先将新元素添加到rear所指的位置,然后再讲rear加1;因而rear指示了实际队尾位置的下一个位置,即下一个元素应当加入的位置。如上图的图b所示。
  3. 当队首元素出队时,先将队首元素记录下来,然后front指针加1,指示新的队头的位置,然后将记录的元素值返回,如上图c所示。

2.顺序队列存在的问题

如果上图图c中B再出队,则front会在C的位置,这样原来的索引为0和1的内存就会是空的,这时数组的前端还有空位置,这就造成了资源的浪费。所以,为了能充分的利用数组中的存储空间,把数组的前端和后端连接起来,形成一个闭环,这样的队列叫做循环队列,如下图所示

 上图循环队列最多存maxSize=8个元素,这样如果一直往队列中加入新元素,则当rear=maxSize-1=7时,如果再进一个元素,则rear就会变为0;同理,若此时一直往外出队数据,则当H出队后front也会变为0。这样的操作可以利用取余%来实现:

队头指针进1:front = ( front + 1)  %maxSize;

队尾指针进1:rear= (rear + 1)%maxSize;

3.循环队列队满和队空的判断条件

如果循环队列出队速度快于入队速度,则front会追上rear,此时front == rear,队列为空;

如果入队速度快于出队速度,则rear会追上front,但是为了区别于上面的队空条件(front ===rear),我们用(rear + 1)%maxSize  == front来判断是否队满。也就是说,当rear指到front的前一个位置时,我们就认为队列满了,如上图总的最后一个小图所示,此时rear指向的位置不能再存元素了,如果不留这个位置,再入队最后一个新元素,此时rear == front,这时队列实际是满的,可代码会认为这是队空的条件,进而判断此时队列为空,因此产生混淆。所以,当rear指到front的前一个位置时,我们就认为队列满了,即(rear + 1)%maxSize  == front,队满,队列不再能入队新元素。

这样构成的循环队列最多能存放maxSize - 1个元素。

4.队列基类的头文件"Queue.h"

这个头文件定义了队列通用的一些方法,循环队列,包括后面的链式队列均可以继承实现多态。代码如下:

#pragma once
const int maxSize = 50;  //顺序队列的最大元素个数

template <class T>
class Queue
{
public:
	Queue() {};
	~Queue() {};
	virtual bool EnQueue(const T& x) = 0; //新元素入队
	virtual bool DeQueue(T& x) = 0;       //队头元素出队
	virtual bool getFront(T& x) = 0;      //获取队头元素
	virtual bool IsEmpty() const = 0;          //判断队列是否为空
	virtual bool IsFull() const = 0;            //判断队列是否满。顺序队列需要,链式队列不需要,但仍要重写
	virtual int getSize() const = 0;            //求队列元素的个数
};

//用struct定义节点,链式队才用的到
template <class T>
struct LinkNode {
	T data; //数据域
	LinkNode<T> * link; //指针域,指向下一个节点
	LinkNode() //仅初始化指针的构造函数
	{
		LinkNode<T> *ptr = nullptr;
		link = ptr;
	}
	LinkNode(const T& item, LinkNode<T> *ptr = nullptr) //初始化数据和指针成员的构造函数
	{
		data = item;
		link = ptr;
	}
};

5.循环队列头文件“SeqQueue.h”

该头文件定义循环队列的类以及其属性即接口实现,重写了队列基类Queue的函数,实现多态。代码如下;

#pragma once
//顺序队列,存储形式是数组。为了有效利用内存,设计成循环队列
# include <iostream>
# include <assert.h>
# include "Queue.h"
using namespace std;

template <class T>
class SeqQueue :public Queue<T>   //继承时注意Queue也是类模板,后面加<T>区别普通的类
{
public:                                     //如果不加public,权限为private
	SeqQueue(int sz=10);                   //构造函数
	~SeqQueue() { delete[] elements; }  //[]紧跟delete,表明释放的elements地址是一个数组,而不是单个变量
	bool IsFull() const{ return ((rear + 1) % maxSize == 0) ? true : false; }//是否队满
	bool IsEmpty() const { return (rear == front) ? true : false; }  //是否队空
	bool EnQueue(const T& x);           //新元素入队
	bool DeQueue(T& x);                 //队头元素出队
	bool getFront(T& x);                //获取队头元素
	void makeEmpty() { rear = front = 0; }//清空队列(其实队列内容未被修改,只是将指针置0)
	int getSize() const ;                        //求队列元素的个数

	template <class R>
	friend ostream & operator<<(ostream & out, SeqQueue<R> &sq);//重载<<输出队列内容
private:
	int rear, front;   //队尾和队首的指针,同链式栈一样,不是真正的指针,而是下标。front是第一个元素的下标,rear是最后一个元素下一个位置的坐标
	T * elements;      //指向存放数据的数组的指针
	int maxSize;       //队伍最大可容纳元素的个数
};


//构造函数
template <class T>
SeqQueue<T>::SeqQueue(int sz):front(0),rear(0),maxSize(sz)  //这里的sz不允许使用默认参数
{
	//建立一个最大容量为maxSize的空队列
	//rear = front = 0;
	//maxSize = sz;
	elements = new T[maxSize];
	assert(elements != nullptr);//断言函数,括号里的内容成立,则继续执行代码;否则,终止代码的执行
}


//新元素入队尾
template <class T>
bool SeqQueue<T>::EnQueue(const T& x)
{
	if (IsFull() == true)
		return false;   //队列满则插入失败
	elements[rear] = x; //队尾位置插入新元素
	rear = (rear+1)%maxSize; //队尾指针更新
	return true;
}


//队头元素出队
template <class T>
bool SeqQueue<T>::DeQueue(T& x) 
{
	if (IsEmpty()) return false;
	x = elements[front];
	front = (front + 1) % maxSize; //队头指针加一
	return true;
}


//获取队头元素
template <class T>
bool SeqQueue<T>::getFront(T &x)
{
	if (IsEmpty()) return false;
	x = elements[front];
	return true;
}


//求队列元素的个数
template <class T>
int SeqQueue<T>::getSize() const
{
	return (rear - front + maxSize) % maxSize; 
	//加maxSize是为了保证队列只入队且队列入满时rear=0,front=1,此时rear-front=-1,
	//此时元素个数为maxSize-1,由此加上maxSize可以得到正确结果
}


template <class R>
ostream & operator<<(ostream & out, SeqQueue<R> &sq)
{
	out << "front = " << sq.front << ",rear = " << sq.rear << endl;
	for (int i = sq.front; i != sq.rear; i = (i + 1) % sq.maxSize)
	{
		out << i << " : " << sq.elements[i] << endl;
	}
	return out;
}

6.代码测试

放在“循环队列.cpp”文件中,老规矩只做简单测试,代码如下:

#include<iostream>
#include "SeqQueue.h"
using namespace std;
int main()
{

	SeqQueue<int> sq(10);
	for (int i = 0; i < 10; i++)
		sq.EnQueue(i);
	cout << sq << endl;
}

结果如下图所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值