队列(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;
}
本文介绍了队列的基本概念和特点,重点探讨了队列的顺序存储结构,包括其不足之处及如何改进为循环队列,并提供了C语言实现循环队列的代码示例。此外,还简述了队列的链式存储结构及其操作,如入队和出队。
 C语言例程&spm=1001.2101.3001.5002&articleId=124502505&d=1&t=3&u=58737537591b41098ac52b2deb4f0ef6)
502

被折叠的 条评论
为什么被折叠?



