队列的概念:
队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其另一端进行删除操作。
队列中允许进行插入操作的一端称为队尾,允许进行删除操作的一端称为队头。队列的插入操作通常称作入队列,队列的删除操作通常称作出队列。
下图是一个依次向队列中插入数据元素a0,a1,...,an-1后的示意图:
上图中,a0是当前 队头数据元素,an-1是当前 队尾数据元素。
为了避免当只有一个元素时,对头和队尾重合使得处理变得麻烦,所以引入两个指针:front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样的话,当front指针等于rear时,此队列不是还剩一个元素,而是空队列。
顺序队列 队列的顺序存储结构称为顺序队列,顺序队列实际上是运算受限的顺序表,和顺序表一样,顺序队列也是必须用一个数组来存放当前队列中的元素。由于队列的队头和队尾的位置是变化的,因而要设两个指针和分别指示队头和队尾元素在队列中的位置。
顺序队列中的溢出现象:
上图中,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
#include
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;
}
C# 代码实现的循环队列:
namespace DateStructure
{
interface IQueue
{
void CreateQueue(params int[] arrElem);
void clear();
bool isEmpty();
void EnQueue(int insertElem);
int DeQueue();
int getLength();
void show();
}
class CirQueue : IQueue
{
private int[] m_data;
private int m_front; // 循环队列队头指针
private int m_rear; // 循环队列尾指针
private int m_queueMaxLength;
private int m_queueCurrentLength;
public CirQueue(int maxSize)
{
Debug.Assert(maxSize > 0, "不能分配小于1的数组!");
m_data = new int[maxSize];
Debug.Assert(m_data != null, "初始化时,内存分配失败!");
m_queueMaxLength = maxSize;
m_front = m_rear = m_queueCurrentLength = 0;
}
public void CreateQueue(params int[] arrElem)
{
// arrElem.Length 不能等于 m_queueMaxLength,否则但添加元素时,会出现bug
if ((arrElem != null) && (arrElem.Length != 0) && arrElem.Length < m_queueMaxLength)
{
m_rear = m_queueCurrentLength = arrElem.Length;
for (int i = 0; i != m_queueCurrentLength; ++i)
{
m_data[i] = arrElem[i];
}
}
else
{
throw new ArgumentException("循环队列整表创建失败!");
}
}
public void clear()
{
m_front = m_rear = m_queueCurrentLength = 0;
}
public int DeQueue()
{
if (isEmpty() == true)
{
return -1;
}
int removeElem = m_data[m_front];
m_front = (m_front + 1) % m_queueMaxLength;
--m_queueCurrentLength;
return removeElem;
}
public void EnQueue(int insertElem)
{
if ((m_rear + 1) % m_queueMaxLength == m_front)
{
throw new ArgumentException("当前循环队列已满,无法添加元素!");
}
m_data[m_rear] = insertElem;
m_rear = (m_rear + 1) % m_queueMaxLength;
++m_queueCurrentLength;
}
public int getLength()
{
return m_queueCurrentLength;
}
public bool isEmpty()
{
return ((m_queueCurrentLength == 0) && (m_rear == m_front));
}
public void show()
{
if(m_queueCurrentLength == 0)
{
WriteLine("该循环队列中没有元素,无法输出!");
return;
}
int index = m_front;
WriteLine("输出链队列中的所有元素:");
do
{
Write($"{m_data[index]},");
index = (index + 1) % m_queueMaxLength;
} while (index != m_rear);
WriteLine();
}
}
class Program
{
static void Main(string[] args)
{
WriteLine("输入循环队列的最大容量:");
int sizeCapacity = Convert.ToInt32(Console.ReadLine());
var myQueue = new CirQueue(sizeCapacity);
const int arraySize = 5;
int[] myIntArray = new int[arraySize] { 141, 11, 24, 347, 45 };
myQueue.CreateQueue(myIntArray);
myQueue.show();
WriteLine("请输入你想插入的元素值:");
int pushElem = Convert.ToInt32(Console.ReadLine());
myQueue.EnQueue(pushElem);
myQueue.show();
int removeElem = myQueue.DeQueue();
if (removeElem >= 0)
{
WriteLine($"被删除的元素为:{removeElem}");
myQueue.show();
}
else
{
WriteLine("当前循环队列是空的,没有元素可以删除!");
}
if (myQueue.isEmpty())
{
WriteLine("当前的循环队列为空!");
}
else
WriteLine("当前的循环队列非空!");
WriteLine($"\n获取当前循环队列的总个数:{myQueue.getLength()}");
myQueue.clear();
WriteLine($"获取当前循环队列的总个数:{myQueue.getLength()}");
}
}
}