《大话数据结构4》—— 队列的顺序存储结构 (循环队列)—— C++代码实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34536551/article/details/83715216

 队列 


● 队列的概念:

  队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其另一端进行删除操作。

队列中允许进行插入操作的一端称为队尾,允许进行删除操作的一端称为队头。队列的插入操作通常称作入队列,队列的删除操作通常称作出队列。

下图是一个依次向队列中插入数据元素a0,a1,...,an-1后的示意图:

faf7d1e6-07b1-44bb-9752-5fcc113a0648

上图中,a0是当前 队头数据元素,an-1是当前 队尾数据元素。

为了避免当只有一个元素时,对头和队尾重合使得处理变得麻烦,所以引入两个指针:front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样的话,当front指针等于rear时,此队列不是还剩一个元素,而是空队列。


顺序队列


●  顺序队列 队列的顺序存储结构称为顺序队列,顺序队列实际上是运算受限的顺序表,和顺序表一样,顺序队列也是必须用一个数组来存放当前队列中的元素。由于队列的队头和队尾的位置是变化的,因而要设两个指针和分别指示队头和队尾元素在队列中的位置。 

● 顺序队列中的溢出现象:

607ede1a-91b5-4ac9-8850-07078379fddb

上图中,front指针指向队头元素,rear指针指向队尾元素的下一个位置。

图(d)中b、c、d出队后,front指针指向元素e,rear指针在数组外面。假设这个队列的总个数不超过5个,但目前如果接着入队的话,因数组末尾元素已经被占用,再向后加就会产生数组越界的错误,可实际上队列在下标为0、1、2、3、4的地方还是空闲的,我们把这种现象叫做“假溢出”——也叫假上溢。

下溢:队列为空时,做出队操作产生的溢出现象。

真上溢:当队列满时,做入队操作产生的空间溢出现象。

顺序队列的基本操作

入队时:将新元素插入rear所指的位置,并将rear+1。

出队时:删除front所指的元素,并将front+1。


循环队列


●  所以解决假溢出的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种逻辑上首尾相连的顺序存储结构称为循环队列。

如何判断循环队列究竟是空的还是满的:

  现在问题又来了,我们之前说,空队列时,front指针等于rear指针,那么现在循环队列满的时候,也是front等于rear,那么如何判断循环队列究竟是空的还是满的?有如下办法:

办法1:设置一个标志位flag。初始时置flag=0;每当入队列操作成功就置flag=1;每当出队列操作成功就置flag=0。则队列空的判断条件为:rear == front && flag==0;队列满的判断条件为:rear = = front && flag= =1。

办法2:保留一个元素的存储空间。此时,队列满时的判断条件为  (rear + 1) % maxSize == front;队列空的判断条件还是front == rear。

办法3:设计一个计数器count,统计队列中的元素个数。此时,队列满的判断条件为:count > 0 && rear == front ;队列空的判断条件为count == 0。

我们在接下来的代码中采用方法2来实现。

CirQueue.h 头文件

#include<iostream>
#include<cassert>
using namespace std;
#ifndef TT_CIR_QUEUE_H
#define TT_CIR_QUEUE_H
namespace tt
{
	class CirQueue
	{
	public:
		using  ElemType = int;
		using Status = void;

		enum State
		{
			TT_ERROR = 0,
			TT_OK = 1
		};
	public:
		CirQueue(ElemType INI_SIZE);  //初始化队列
		~CirQueue();
		ElemType isEmpty()const;  //判断队列是否为空。
		ElemType isFull()const;  //确定队列是否满了

		ElemType clear(); //将队列清空
		ElemType getHead(ElemType &elemOut); //若队列存在且非空,用elem返回队列的队头元素
		ElemType insert(ElemType elem); //若队列存在,插入新元素elem到队列中并成为队尾元素。
		ElemType remove(ElemType &elemOut);// 删除队列中的队头元素,并用elem返回。

		ElemType destroy(); //若队列存在,则销毁它
		Status getLength()const; //返回队列中当前元素的个数。
		Status show();  //显示队列中的所有元素
	private:
		ElemType *m_data;  //数据域
		ElemType m_front;  //指向队头元素
		ElemType m_rear;  //指向队尾元素的下一个位置
		ElemType m_queueSize;   //队列的最大容量
	};
	inline CirQueue::ElemType CirQueue::isEmpty()const  //判断队列是否为空
	{
		return (m_front == m_rear);
	}
	inline CirQueue::Status CirQueue::getLength()const //返回队列中当前元素的个数
	{
		cout<< "当前队列中的元素个数为:" << (m_rear - m_front + m_queueSize) % m_queueSize << endl;
	}
	inline CirQueue::ElemType CirQueue::clear() //将队列清空
	{
		m_front = m_rear = 0;
		return TT_OK;
	}
	inline CirQueue::ElemType CirQueue::isFull()const  //确定队列是否满了
	{
		return (m_rear + 1) % m_queueSize == m_front;
	}
}
#endif  //TT_CIR_QUEUE_H

testCirQueue.cpp 源文件

#include"CirQueue.h"

namespace tt
{
	CirQueue::CirQueue(ElemType INI_SIZE)
	{
		assert(INI_SIZE != 0);
		m_data = new int[INI_SIZE];
		assert(m_data != nullptr);
		m_queueSize = INI_SIZE;  //把队列的初始化的最大容量赋值给成员数据
		m_front = m_rear = 0;  //m_front等于m_rear 就是空队列
		cout << "***********     循环队列初始化成功!   **************" << endl;
	}
	CirQueue::~CirQueue()
	{
		this->destroy();
	}
	CirQueue::ElemType CirQueue::insert(ElemType elem)  //插入元素致队列的尾部
	{
		if (((m_rear + 1) % m_queueSize) == m_front)//判断队列满的情况
		{
			return TT_ERROR;
		}
		m_data[m_rear] = elem;    //将元素elem  添加到队列的末尾
		m_rear = (m_rear + 1) % m_queueSize;  //尾指针应以此种方式加1,才会实现循环队列, 若到末尾转到数组的头部
		return TT_OK;
	}
	CirQueue::ElemType CirQueue::remove(ElemType &elemOut)  //删除队列的队头元素
	{
		if (m_front == m_rear)   //判断循环队列是否为空
		{
			return TT_ERROR;
		}
		elemOut = m_data[m_front];  //将对头元素赋给elem返回
		m_front = (m_front + 1) % m_queueSize;  //m_front指针向后移动一位,若到最后则转到数组头部
		return TT_OK;
	}
	CirQueue::ElemType CirQueue::getHead(ElemType &elemOut) //若队列存在且非空,用elem返回队列的队头元素
	{
		if (m_front == m_rear)   //判断循环队列是否为空
		{
			return TT_ERROR;
		}
		elemOut = m_data[m_front];  //把队头元素用elem返回
		return TT_OK;
	}

	CirQueue::ElemType CirQueue::destroy() //若队列存在,则销毁它
	{
		delete[] m_data;
		m_data = nullptr;
		m_front = m_rear = m_queueSize = 0;
		return ((!m_data) && (m_front == m_rear == m_queueSize == 0));  //多一个判断看队列是否被销毁
	}
	CirQueue::Status CirQueue::show()  //显示队列的所有元素
	{
		if (m_front == m_rear)
		{
			cout << "错误,此队列中没有数据或者队列没有建立,无法显示!" << endl;
		}
		else
		{
			 auto count = (m_rear - m_front + m_queueSize) % m_queueSize;  //一个临时变量存储该队列的元素个数
			cout << "队列从队头至队尾内容依次为:";
			for (size_t i = m_front; i < m_front + count; ++i)
			{
				cout << m_data[i] << ' ';
			}
			cout << endl;
		}
	}
}

//测试循环队列的功能
void testCirQueue()
{
	int allocMemory(0);
	cout << "请输入队列初始化的最大容量:";
	cin >> allocMemory;
	tt::CirQueue myCirQueue(allocMemory);  //初始化一个队列
	while (true)
	{
		{
			cout << ("\n***************************************************") << endl
				<< "***************     循环队列的基本功能展示    **************" << endl
				<< "*******************************************************" << endl
				<< "**************      选择1—— 数据进队列尾.       ************" << endl
				<< "**************      选择2—— 删除队列头元素.     ************" << endl
				<< "***************     选择3—— 显示队列头元素.     ************" << endl
				<< "***************     选择4—— 判断队列是否为空.   ************" << endl
				<< "***************     选择5—— 判断队列是否满了.   ************" << endl
				<< "***************************************************************" << endl
				<< "***************     选择6—— 显示队列的元素个数. *************" << endl
				<< "***************     选择7—— 清空队列.           *************" << endl
				<< "****************     选择8—— 销毁队列.           *************" << endl
				<< "****************     选择9—— 显示队列中的所有元素. ***********" << endl
				<< "****************     选择10—— 清屏.              *************" << endl
				<< "****************     选择0—— 退出程序!         *************" << endl
				<< "***************************************************************" << endl
				<< "***************************************************************" << endl;
		}
		cout << "\n*****************     请输入你想要使用的队列功能的序号   ***************" << endl;
		cout << "请输入你的选择:";
		int userChoice(0);
		cin >> userChoice;
		if (!userChoice)
		{
			cout << "程序已退出,感谢您的使用!" << "\n" << endl;
			break;
		}

		switch (userChoice)
		{
		case 1:
		{
			int pushDatas(0);
			cout << "请输入想要添加的数据:";
			cin >> pushDatas;
			if (myCirQueue.insert(pushDatas))   //进队列
			{
				cout << "数据" << pushDatas << "成功进入队列中!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();  //显示所有元素
			}
			else
				cout << "目前队列已满, 数据" << pushDatas << "进入失败!" << endl;
			break;
		}
		case 2:
		{
			int popDatas(0);
			if (myCirQueue.remove(popDatas))   //删除队列头元素
			{
				cout << "数据" << popDatas << "从队列中成功删除!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			else
			{
				cout << "目前队列为空, 数据" << popDatas << "删除失败!" << endl;
				myCirQueue.getLength();
			}
			break;
		}
		case 3:
		{
			int disHead(0);
			if (myCirQueue.getHead(disHead))   //获取队头元素
			{
				cout << "队列头元素为:" << disHead << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			else
			{
				cout << "目前队列为空, 数据" << disHead << "获取失败!" << endl;
				myCirQueue.getLength();
			}
			break;
		}
		case 4:
			if (myCirQueue.isEmpty())  //判断队列是否空
			{
				cout << "队列为空,或者队列尚未建立!" << endl;
				myCirQueue.getLength();
			}
			else
			{
				cout << "队列非空!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			break;
		case 5:
			if (myCirQueue.isFull())  //判断队列是否满
			{
				cout << "目前队列已满,不能再添加数据了!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			else
			{
				cout << "目前队列不满,还可以继续输入数据进栈!" << "\n" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			break;
		case 6:
			myCirQueue.getLength();  //显示队列的元素个数
			myCirQueue.show();
			break;

		case 7:
			if (myCirQueue.clear())
			{
				cout << "队列已清空!" << endl;
				myCirQueue.getLength();
			}
			else
			{
				cout << "队列清空失败!" << endl;
				myCirQueue.getLength();
				myCirQueue.show();
			}
			break;
		case 8:
		{
			cout << "你确定要销毁一个队列吗?(若销毁请输入输入(Y/y))";
			char yesOrNo;
			cin >> yesOrNo;
			if ((yesOrNo == 'Y') || (yesOrNo == 'y'))
			{
				if (myCirQueue.destroy())
				{
					cout << "队列已被销毁!" << endl;
				}
				else
					cout << "队列销毁失败!" << endl;
			}
			break;
		}
		case 9:
			myCirQueue.getLength();
			myCirQueue.show();
			break;
		case 10:
			system("cls");
			cout << "屏幕已经清屏,可以重新输入!" << "\n" << endl;
			break;
		default:
			cout << "输入的序号不正确,请重新输入!" << "\n" << endl;
		}
	}
}
int main()
{
	testCirQueue();
	system("pause");
	return 0;
}

● 注意: 把以上代码复制黏贴到 Visual Studio 2017 上 或者 Visual Studio 2015 上,如果是其他的编译器,可能会出错, 因为该程序用了一点 C++11 的语法, 如果你的编译器不遵循C++11的语法的法,一般来说会出错。

把该代码分别放进编译器中的源文件和头文件就能编译运行成功。  如果要合成到一块的源文件的话, 需要修改一下地方。你们自己修改吧。

如果有错误的话,欢迎指教。

如果转载的话, 请指明出处。谢谢。
 

猜你喜欢

转载自blog.csdn.net/qq_34536551/article/details/83715216
今日推荐