队列(queue)是只允许在一段进行插入操作,而在另一端进行删除操作的线性表。具有先进先出的特点,允许插入的一段称为队尾,允许删除的一端称为队头。
队列的顺序存储结构
顺序存储的不足
首先建立一个长度为n的数组,队列中存储的数据不大于n。在队列中添加一个元素入下图所示
在队尾后追加一个元素,即可。那么删除一个元素呢?在队头出删除一个元素后,前面会空出一个位置,因此将后面的元素均向前移动一位。
但是这样会随着队列中的数据越来越多,而使得每次删除一个元素,都需要移动全部元素,对性能影响比较大。
所以不妨可以移动队头,这样操作就简单的多。
为了避免当队列中没有元素和只有一个元素时,队头和队尾重合,不妨将队尾向后移动一位,指向最后一个元素的下一位空位置。
这样的存储方式也有很多问题,第一,当数组第5位添加一个元素时,队尾应该指向哪里?第二,随着队列的元素删除,数组前面的位置出现大量的空位置,因此引入循环队列,将数组的头和尾连接起来,这样就可以很好的处理上述问题。
循环队列
需要注意的是,因为循环队列在队列满员时,队头和队尾是重合的,这显然和空队列时是混淆的,因此我们要保留一个空闲位置,认为此时队列已满。
现在实现循环队列的代码。
顺序存储结构如下:
#define MAXSIZE (20)
typedef struct
{
int data[MAXSIZE]; //数组,类型为int,可自行定义
int front; //队头-数组的下标
int rear; //队尾
}sqQueue;
队列的初始化代码:
/* 初始化为空队列 */
void InitQueue(sqQueue *Q)
{
Q->front = 0;
Q->rear = 0;
}
计算队列的长度:
思路:
(1)当队尾rear
大于等于队头front
时,队列的长度等于rear - front
;
(2)当队尾rear
小于队头front
时,队列的长度等于rear + MAXSIZE - front
。
/* 返回队列的长度 */
int GetQueueLength(sqQueue Q)
{
return (MAXSIZE + Q.rear - Q.front) % MAXSIZE;
}
入队操作如下:
/*********************************************\
*function: 入队列,将一个数据放入队列中
*input: int data - 数据
*output: sqQueue *Q - 队列指针
*return: bool - 布尔遍历(TRUE,FALSE)
\*********************************************/
bool PutQueue(sqQueue *Q, int data)
{
assert(Q != NULL); //检查Q是否为空指针
if ((Q->rear + 1) % MAXSIZE == Q->front) //是否满员
{
return FALSE;
}
Q->data[Q->rear] = data; //存入数据
Q->rear = (Q->rear + 1) % MAXSIZE; //队尾rear向后移一位
return TRUE;
}
出队操作如下:
/*********************************************\
*function: 出队列,从队列中取出一个数据
*input: sqQueue *Q - 队列指针
*output: int data - 数据
*return: bool - 布尔遍历(TRUE,FALSE)
\*********************************************/
bool GetQueue(sqQueue *Q, int *data)
{
assert(Q != NULL);
if (Q->rear == Q->front) //是否为空队列
{
return FALSE;
}
*data = Q->data[Q->front]; //将队头数据赋给*data
Q->front = (Q->front + 1) % MAXSIZE; //队头front向后移一位
return TRUE;
}
队列的链式存储结构
队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而异,我们把他们简称为链队列。我们将队头指向链表的头结点,队尾指向链表的终端结点。
链表操作可以参考线性表总结(顺序存储、链表)
存储结构图如下:
当队列为空时,front
和rear
均指向头结点。
入队
入队操作如下:
bool PutQueue_Link(LinkQueue *Q, int data)
{
QNode *s = (QNode *)malloc(sizeof(QNode));
if (NULL == s)
{
return FALSE;
}
s->data = data; //新结点赋值
s->next = NULL;
Q->rear->next = s; //当前队列尾结点的next指向新结点
Q->rear = s; //将新结点作为队列的尾结点
return TRUE;
}
出队
出队操作如下:
bool GetQueue_Link(LinkQueue *Q, int *data)
{
QNode *p;
if (Q->front == Q->front) //空队列
{
return FALSE;
}
p = Q->front->next;
*data = p->data; //队头数据赋给*data
Q->front->next = p->next;
if (Q->rear == p) //如果原队列只有一个元素,则将队尾指针指向头结点
{
Q->rear = Q->front;
}
free(p); //删除结点
return TRUE;
}