队列的定义
队列(queue)是只允许在一端进行插入操作,另一端进行删除操作的线性表
队列是一种先进先出的线性表,允许插入的一端称为队尾(rear),允许删除的一端称为队头(front)。向队列中插入元素称为入队,从队列中删除元素称为出队。当队列中没有元素时称为空队列。队列的操作是按先进先出的原则进行的,即新添加的元素总是加到队尾,每次离开的元素总是队头的元素。和栈一样,队列也是一种运算受限制的线性表,所以又叫先进先出表(First In First Out),简称FIFO表。
顺序队列
假设一个队列有n个元素,则顺序存储的队列需要建立一个大于n的数组,并把队列的所有元素存储在数组的前n个单元,数组下标为0的一端即为队头。
所谓的入队,就是在队尾追加一个元素,不需要移动任何元素,所以时间复杂度为O(1).
队列的出队是在队头,即下标为0的位置,也就意味着,队列中的所有位置都得向前移动,以保证下标为0的位置,即对头不为空。此时时间复杂度为O(n)。
为了避免当只有一个元素时,队头和队尾重合使得处理变得麻烦,所以引入两个指针,front指针 指向队头元素,rear指针 指向队尾元素的下一个元素。这样当 front 等于 rear 时,不是队列中有一个元素,而是表示空队列。
假设数组的长度为5,空队列及初始状态如图所示,front与rear指针都指向下标为0的位置。当队列中有4个元素时,front指针不变,rear指针指向下标为4的位置。
此时出队两个元素,则front指针指向下标为2的位置,rear不变。再入队一个元素,front指针不变,此时rear指针移动到数组之外
假设这个队列中的总个数不超过5个,但目前如果接着入队的话,会导致数组越界的错误,但是队列在下标为0和1的位置是没有元素的。我们把这种现象叫做“假溢出”。
循环队列
解决“假溢出”的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。
为了解决“假溢出”的问题,我们引入循环队列。
接着入队两个元素,会发现rear指针与front重合了。
解决办法为:当队列空时,判断条件就是 rear == front, 当队列满时,我们修改其判断条件,保留一个元素空闲。也就是说,队列满时,数组中还有一个空闲单元。以下两种情况,我们都认为队列已经满了。
循环队列的顺序存储结构如下:
#define SIZE 8
typedef struct Queue
{
int elem[SIZE]; // 存放队列元素
int front; // 队头
int rear; // 队尾
}Queue,*QueueS;
基本操作:
void InitQueueS(QueueS queue); // 初始化循环队列
bool Push(QueueS queue, int val); // 入队
bool Pop(QueueS queue, int *rtv); // 出队
void Show(QueueS queue); // 打印队列元素
int GetLength(QueueS queue); // 计算队列长度
bool IsEmpty(QueueS queue); // 判空
void Clear(QueueS queue); // 清空队列
void InitQueueS(QueueS queue)
{
assert(queue != NULL);
queue->front = 0;
queue->rear = 0;
}
static bool IsFull(QueueS queue)
{
return (queue->rear + 1) % SIZE == queue->front;
}
bool Push(QueueS queue, int val)
{
if (IsFull(queue))
{
return false;
}
queue->elem[queue->rear] = val;
queue->rear = (queue->rear + 1) % SIZE;
return true;
}
bool Pop(QueueS queue, int *rtv)
{
if (IsEmpty(queue))
{
return false;
}
if (rtv != NULL)
{
*rtv = queue->elem[queue->front];
}
queue->front = (queue->front + 1) % SIZE;
return true;
}
void Show(QueueS queue)
{
for (int i = queue->front; i != queue->rear; i = (i + 1) % SIZE)
{
printf("%d ", queue->elem[i]);
}
printf("\n");
}
int GetLength(QueueS queue)
{
return (queue->rear - queue->front + SIZE) % SIZE;
}
bool IsEmpty(QueueS queue)
{
return queue->front == queue->rear;
}
void Clear(QueueS queue)
{
queue->front = queue->rear;
}