队列(Queue):一种先进先出(first in first out)的线性表。只允许在尾部(rear)插入,头部(front)删除。
上图可以形象的描述队列的特点。数据元素排队进入队列,按顺序排队出队列。
实现顺序队列时,可以采用一组地址连续的地址单元,例如数组。队列头(front)与队尾(rear)在初始化时,指向数组的"0"地址。下面为插入新的元素时的操作:
入队操作:不需要移动任何元素,在队尾追加一个元素。故时间复杂度为O(1);
出对列操作:
出对列操作:front指向数组0地址。即队列头部始终在0地址。删除其中元素后,为保证头部单元“内容”不为空。需要将后面的元素往前移动。这样,需要的时间复杂度为:O(n);
那么为了避免繁琐的将后面的元素向前移动,可以规定front不必非得指向数组中下标为0的位置。如下图:
front指向下一个元素。删除一个,移动一次。
当fornt ==rear 时,可以是空队列,也可以是"只剩下一个元素的队列”
故为了避免这种情况,
规定:front指针指向对头元素,rear指针指向最后元素的下一位置(队尾元素的下一位置)。这样:front == rear 是为空队列。
下面为长度为5的数组,初始状态 front == rear。依次插入新元素。元素a1,a2出队列后,在插入a5,情况如图:
rear所指超出界限。但是下标0,1出为空。
难道不利用了吗?还要将增加数组的长度吗?那多大的数组才合适呢?
前面的空单元没有利用。此现象称之为"假溢出"。为了解决这种问题。循环队列就呼之欲出了。
循环队列定义:首尾相接的顺序存储结构。
那么当上面的情况出现在循环队列里时,如图:rear指向下标0处。
为了避免rear == front 判断是空,还是满队列时。
常用的方法是规定:
(1) 空队列:front == rear;
(2)队列满:保留一个元素空间,即有一个单元为空。
故队列满时:(rear + 1 + QueueSize) % QueueSize == front /*QueueSize 为队列的分配空间*/
(3)队列长度,即队列元素个数:(rear -front + QueueSize) % QueueSize
(4)插入新的元素后:rear = (rear+ 1) % QueueSize
(5)删除元素后: front ==(front + 1) % QueueSize
有了上述公式后,循环队列的顺序结构实现就清晰明了了。
代码:
typedef int Status;
typedef int QElemType;
typedef struct Queue
{
QElemType data[MAXSIZE];
int front;
int rear;
}SQueue;
Status Visit(QElemType q)
{
printf("%3d",q);
return OK;
}
/*
****操作结果:初始化队列
*/
Status InitQueue(SQueue *Q)
{
Q->front = 0;
Q->rear = 0;
return OK;
}
/*
****操作结果:将非空队列清空
*/
Status ClearQueue(SQueue *Q)
{
Q->front = Q->rear = 0;
return OK;
}
/*
****操作结果:若队列空,则返回TRUE,否则返回FALSE
*/
Status EmptyQueue(SQueue Q)
{
if(Q.front == Q.rear)
return TRUE;
else
return ERROR;
}
/*
****操作结果:返回队列元素个数
*/
int LengthQueue(SQueue Q)
{
return (Q.rear - Q.front +MAXSIZE)%MAXSIZE;
}
/*
****操作结果:向未满的队列尾部插入新的数据元素
*/
Status InsertQueue(SQueue *Q,QElemType e)
{
if((Q->rear + 1+ MAXSIZE)%MAXSIZE == Q->front) //队列满
return ERROR;
Q->data[Q->rear] = e;
printf("插入新的数据元素:%d\n",e);
Q->rear = (Q->rear + 1)%MAXSIZE;
return OK;
}
/*
****操作结果:若队列非空,则删除队列头部元素,并用e返回,成功OK,失败ERROR
*/
Status DeleteQueue(SQueue *Q,QElemType *e)
{
if(Q->front == Q->rear) //队列空
return ERROR;
*e = Q->data[Q->front];
printf("删除的队列头部元素为:%d\n",*e);
Q->front = (Q->front + 1)%MAXSIZE;
return OK;
}
/*
****操作结果:得到非空队列的头部元素
*/
Status GetHead(SQueue Q,QElemType *e)
{
if(Q.front == Q.rear)
return ERROR;
*e = Q.data[Q.front];
printf("得到的队列头元素为:%d\n",*e);
return OK;
}
/*
****操作结果:从对列头到队列尾一次输出队列数据元素
*/
Status Traverse(SQueue Q)
{
int i ;
i = Q.front;
printf("将队列元素输出:\n");
while((i + Q.front) != Q.rear)
{
Visit(Q.data[i]);
i = (i + 1)%MAXSIZE;
}
printf("\n");
return OK;
}
下面为队列链式实现:
typedef struct QNode
{
QElemType data;
struct QNode *next;
}QNode ,*QueuePtr; //*QueuePtr 指针类型的结点
typedef struct
{
QueuePtr front; //头指针
QueuePtr rear; //尾指针
}LinkQueue;
Status Visit(QElemType q)
{
printf("%3d",q);
return OK;
}
/*
****操作结果:初始化 一个带头结点的队列
*/
Status InitQueue(LinkQueue *Q)
{
Q->front = Q->rear = (QueuePtr)malloc(MAXSIZE*sizeof(QNode));
if(!Q->front)
exit(OVERFLOW);
Q->front->next = NULL;
return OK;
}
/*
****操作结果:判断队列是否为空,空 返回TRUE,否则返回FALSE
*/
Status EmptyQueue(LinkQueue Q)
{
if(Q.front == Q.rear)
return TRUE;
else
return FALSE;
}
/*
**** 操作结果:销毁队列
*/
Status DestroyQueue(LinkQueue *Q)
{
while(Q->front)
{
Q->rear = Q->front;
free(Q->front);
Q->front = Q->rear;
}
return OK;
}
/*
****操作结果:清空队列
*/
Status ClearQueue(LinkQueue *Q)
{
QueuePtr p,q;
p = Q->front->next;
while(p)
{
q = p->next;
free(p);
p = q;
}
Q->rear = Q->front;
Q->front->next = NULL;
return OK;
}
/*
****返回队列元素个数
*/
int LengthQueue(LinkQueue Q)
{
int i = 0;
QueuePtr p;
p = Q.front;
while(p != Q.rear) //不存在队列满情况
{
p = p->next;
i++;
}
return i;
}
/*
****操作结果:向队尾插入的新的数据元素
*/
Status InsertQueue(LinkQueue *Q,QElemType e)//不存在队列满,故不用判断
{
QueuePtr p;
p = (QueuePtr)malloc(sizeof(QNode));
if(!p)
exit(OVERFLOW);
p->data = e;
p->next = NULL;
Q->rear->next = p;
Q->rear = p;
return OK;
}
/*
****操作结果:若队列非空,删除对头元素
*/
Status DeleteQueue(LinkQueue *Q,QElemType *e)
{
QueuePtr p;
if(Q->front == Q->rear) //队列空
return ERROR;
p = Q->front->next; //p指向队列第一个结点,Q->front 代表头结点
*e = p->data;
Q->front->next = p->next;
if(Q->rear == p) /*若队列只有一个结点,即此时 p 与 rear指向同一结点。
**** 删除p 结点后,应该将rear 指向fornt.*/
Q->rear = Q->front;
free(p);
return OK;
}
/*
****操作结果:若队列非空,用e返回对头的数据元素
*/
Status GetHead(LinkQueue Q,QElemType *e)
{
QueuePtr p;
if(Q.front == Q.rear)
return ERROR;
p = Q.front->next;
*e = p->data;
return OK;
}
/*
****操作结果:从对头至队尾一次输出队列元素
*/
Status Traverse(LinkQueue Q)
{
QueuePtr p;
if(Q.front == Q.rear)
return ERROR;
p = Q.front->next;
while(p)
{
Visit(p->data);
p = p->next;
}
printf("\n\n");
return OK;
}
参考了《大话数据结构》一书,作者讲解的清晰明了。适合入门,以及知识点回顾。