队列(Queue)
介绍
队列是一种有序列表,只允许对队尾(rear)进行删除操作,对队首(front)进行删除操作,即先入先出(FIFO)。
实现队列需要的内部元素
元素 | 含义 |
---|---|
maxSize | 代表能够存储的数据的个数 |
front | 指向队首 |
rear | 指向队尾 |
array[] | 用于存储数据 |
void Enqueue(int n) | 将元素n入队 |
int Dequeue() | 将队尾元素出队,同时元素个数-1 |
int Peek() | 返回队尾元素,元素个数不变 |
bool IsFull() | 队列已满返回true,未满返回false |
bool IsEmpty() | 队列为空返回true,不为空返回false |
数组模拟一般队列
内部元素含义
元素 | 含义 |
---|---|
maxSize | 代表能够存储的数据的个数 |
front | 指向数组内第一个元素的前一位置 |
rear | 指向数组内最后一个元素的位置 |
array[] | 用于存储数据 |
void Enqueue(int n) | 将元素n入队 |
int Dequeue() | 将队尾元素出队,同时元素个数-1 |
int Peek() | 返回队尾元素,元素个数不变 |
bool IsFull() | 队列已满返回true,未满返回false |
bool IsEmpty() | 队列为空返回true,不为空返回false |
分析思路
入队
1.判断队列是否已满,如果已满则直接返回
2.如果未满则将元素加入队列,并将rear向后移
出队
1.判断队列是否为空,如果为空抛出异常
2.如果不为空则返回下标为 front+1 的元素,并将front后移
Peek
1.判断队列是否为空,如果为空抛出异常
2.如果不为空则返回下标为 front+1 的元素
IsFull
rear == maxSize-1 时返回true
IsEmpty
rear == front 时返回true
图解
代码实现
class ArrayQueue
{
private int _maxSize; //最大容量
private int _front; //队列头 指向队列头的前一个位置
private int _rear; //队列尾 指向队列尾的数据
private int[] _arr; //用于存放数据的数组
public ArrayQueue(int maxSize)
{
this._maxSize = maxSize;
this._front = -1;
this._rear = -1;
_arr = new int[_maxSize];
}
//判断是否已满
public bool IsFull()
{
return _rear == _maxSize - 1;
}
//判断队列是否为空
public bool IsEmpty()
{
return _rear == _front;
}
//入队
public void Enqueue(int n)
{
//判断队列是否已满
if (IsFull())
{
Console.WriteLine("队列已满,不能增加数据");
return;
}
_arr[++_rear] = n;
}
//出队
public int Dequeue()
{
//判断队列是否为空
if (IsEmpty())
{
throw new Exception("队列为空");
}
return _arr[++_front];
}
//显示头数据
public int Peek()
{
//判断是否为空
if (IsEmpty())
{
throw new Exception("队列为空");
}
return _arr[_front + 1];
}
}
总结
可以看出,当rear到达数组的最后一个位置,而front指向数组中间位置时,front之前的空间就是浪费的 (如上图),而此种方法实现的队列做不到数组空间的复用。
虽然在数组的前半部分仍然有未被利用的空间,但是当想要继续往队列中添加元素时就会产生数组越界的错误,也被称作“假溢出”。
因而出现了环形队列。
环形队列
什么是环形队列
为了达到数组空间的复用,或者说是为了解决“假溢出”的问题,我们把队列的头部和尾部相连接,而这种头尾相接,可以看作是一个“环”的队列就是环形队列。
图解
假设这样一个场景,先将队列存满,然后两次出队,然后再向队列中添加两个元素,可以得到这样一个结果
rear == front == 1 如图所示
当队列为空和队列已满时 rear 都等于 front,此时判断队列是空队列还是满队列的方法有两种
1.设置一个标记变量,当 front == rear 且 flag == false 时队列为空,当 front == rear 且 flag == true 时队列为满。
2. 当队列为空时 front == rear, 当队列为满时修改其条件,保留一个空置空间。也就是说队列满的时候数组中还有一个空闲的空间,此时就认为队列时满的。
下面介绍第二种解决方案
内部元素含义
元素 | 含义 |
---|---|
maxSize | 代表能够存储的数据的个数+1 |
front | 指向数组内第一个元素 |
rear | 指向数组内最后一个元素后一位置 |
array[] | 用于存储数据 |
void Enqueue(int n) | 将元素n入队 |
int Dequeue() | 将队尾元素出队,同时元素个数-1 |
int Peek() | 返回队尾元素,元素个数不变 |
bool IsFull() | 队列已满返回true,未满返回false |
bool IsEmpty() | 队列为空返回true,不为空返回false |
int GetSize() | 获取数组中有效值的个数 |
分析思路
入队
1.判断队列是否已满,如果已满则直接返回
2.如果未满则将元素加入队列,并将rear向“后移”
后移代码
_rear = (_rear + 1) % _maxSize; //通过取模来达到 “循环” 的目的
出队
1.判断队列是否为空,如果为空抛出异常
2.如果不为空则返回下标为 front 的元素,并将front后移
后移代码
_front = (_front + 1) % _maxSize; //通过取模来达到 “循环” 的目的
Peek
1.判断队列是否为空,如果为空抛出异常
2.如果不为空则返回下标为 front 的元素
IsFull
当 (rear+1) % maxSize == front 时队列已满(返回 true)
IsEmpty
rear == front 时返回true
GetSize
当 rear > front 时,有效元素的个数为 rear - front
而当 rear < front 时,此时可以将数组看成两段,第一段是 front 到 maxSize,第二段是 rear - 0 ,数组中有效元素的个数是 rear - front + maxSize
综上可得,队列中有效元素的个数为
(rear - front + maxSize) % maxSize
代码实现
class CircleQueue
{
private int _maxSize; //最大容量
private int _front; //队列头 指向第一个元素
private int _rear; //队列尾 指向最后一个元素的后一位置
private int[] _arr; //用于存放数据的数组
public CircleQueue(int maxSize)
{
this._maxSize = maxSize;
_arr = new int[_maxSize];
_front = 0;
_rear = 0;
}
//判断是否已满
public bool IsFull()
{
return ((_rear + 1) % _maxSize) == _front;
}
//判断队列是否为空
public bool IsEmpty()
{
return _rear == _front;
}
//添加数据
public void Enqueue(int n)
{
//判断队列是否已满
if (IsFull())
{
Console.WriteLine("队列已满,不能增加数据");
return;
}
//此时rear指向的是最后一个元素的后一位置,因此可以直接添加
_arr[_rear] = n;
//将 rear 后移
_rear = (_rear + 1) % _maxSize;
}
//出队
public int Dequeue()
{
//判断队列是否为空
if (IsEmpty())
{
throw new Exception("队列为空");
}
//此时front 指向第一个元素
int val = _arr[_front];
//将front后移
_front = (_front + 1) % _maxSize;
return val;
}
//求出当前队列有效数据的个数
private int GetSize()
{
return (_rear + _maxSize - _front) % _maxSize;
}
//显示头数据
public int Peek()
{
//判断是否为空
if (IsEmpty())
{
throw new Exception("队列为空");
}
return _arr[_front];
}
}
两种队列内部元素的对比
元素 | 环形队列 | 顺序存储结构队列 |
---|---|---|
maxSize | 实际能够存储的元素个数为maxSize-1 | 代表能够存储的数据的个数 |
front | 指向数组内第一个元素 | 指向数组内第一个元素的前一位置 |
rear | 指向数组内最后一个元素后一位置 | 指向数组内最后一个元素 |
bool IsFull() | (rear+1) % maxSize == front | rear == maxSize -1 |
bool IsEmpty() | rear == front | rear == front |
int GetSize() | (rear - front + maxSize) % maxSize | rear - front |