队列
只允许在一端进行插入操作,而在另一端进行删除操作的线性表,即是一种先进先出(FIFO)的线性表。
抽象数据类型
ADT 队列(Queue)
Data
元素具有相同的类型,相邻的元素具有前驱和后继的关系
Operation
InitQueue(*Q) //初始化操作,建立一个空队列Q
DestroyQueue(*Q) //若队列Q存在,则销毁它
ClearQueue(Q) //将队列Q清空
QueueEmpty(Q) //若队列为空,返回true,否则返回false
GetHead(Q,*e) //若队列存在且非空,用e返回队列Q的队头元素
EnQueue(*Q,e) //若队列Q存在,插入新元素e到队列Q的队尾
DeQueue(*Q,*e) //删除队列Q的队尾元素,并用e返回其值
QueueLength(Q) //返回队列Q的元素个数
endADT
循环队列
队列顺序存储的不足
若使用数组存储队列,下标为0为开始,为n-1为末尾,为了减少操作的复杂度,定义两个指针,front指向队头的元素,rear指向队尾元素的下一个元素,当两指针相等时,队列为空。
缺点在于随着插入和删除操作,rear指针可能移动到数组之外,也称为假溢出,即front指针前面还有空位未被利用。
为解决上述为题,当后面满了,就再从头开始,也就是头尾衔接,这种头尾相接的顺序结构称为循环队列
判断队列是为空队列还是满队列
方法一:设置标志变量flag来判断
方法二:保留一个空闲单元,此时为满,即front = rear时队列为空,(rear+1)%QueueSize == front时队列为满
通用的队列长度计算公式:(rear-front+QueueSize)%QueueSize
结构代码
typedef int QElemType;
typedef struct
{
QElemType data[MAXSIZE]; //数组
int front; //头指针
int rear; //尾指针,队列不空指向队列尾元素的下一个位置
}SqQueue;
初始化代码
Status InitQueue(SqQueue *Q)
{
Q->front = 0;
Q->rear = 0;
return ok;
}
求队列长度
int QueueLength(SqQueue Q)
{
return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
}
入队列
Status EnQueue(SqQueue *Q,QElemType e)
{
if((Q->rear+1)%MAXSIZE == Q->front) //队列满
return error;
Q->data[Q->rear] = e; //e赋给队尾
Q->rear=(Q->rear+1)%MAXSIZE; //指针后移一位
return ok;
}
出队列
Status EnQueue(SqQueue *Q,QElemType *e)
{
if(Q->rear == Q->front) //队列空
return error;
*e = Q->data[Q->front]; //队头元素赋给e
Q->front=(Q->front+1)%MAXSIZE; //front指针后移一位
return ok;
}
队列的链式存储结构
本质就是单链表,只是只能尾进头出,简称链队列
头指针front指向头节点,尾指针rear指向尾节点,空队列时,均指向头节点
结构定义
typedef int QElemType;
typedef struct QNode //节点结构
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct //队列的链表结构
{
QueuePtr front,rear; //队列的头尾指针
}LinkQueue;
入队操作
Status EnQueue(SqQueue *Q,QElemType e)
{
QueuePtr s = (QueuePtr)malloc(sizeof(QNode)); //建立新节点
if(!s) //内存分配失败
exit(OVERFLOW);
s->data = e; //e赋给队尾
s->next= NULL;
Q->rear->next = s; //原本节点的下一项指向新节点s
Q->rear = s; //把当前队尾节点指向s
return ok;
}
出队操作
Status DeQueue(LinkQueue *Q,QElemType *e)
{
QueuePtr p;
if(Q->front == Q->rear) //空栈
return error;
p = Q->front->next //p指向队首(即头指针下一个)
*e = p->data; //获取数据
if(Q->rear == p) //若该队首也是队尾
Q->rear = Q->front; //尾指针也指向头节点
free(p); //释放队首的空间
return ok;
}
循环队列与链队列的出入操作均只有一个复杂度O(1),但是空间上循环必须有一个固定的长度,而链队列又有指针域会导致一些空间开销,故在队列长度最大值确定的情况下,建议使用循环队列,而无法估计最大长度时,使用链队列。