数据结构与算法-队列
2020-05-18 本文已影响0人
MrDemon_
队列
队列是一种“先进先出”(First-In First-Out,FIFO)的数据结构:即插入在表一端进行,而删除在表的另一端进行,这种数据结构被称为队或队列(Queue),允许插入的一端称为队尾(rear),允许删除的一端称为队头(front)。

顺序队
顺序存储的队列称为顺序队。因为队列的队头和队尾都是活动的,因此,除了队列的数据区外还有队头、队尾两个指针。
顺序队的操作如图
顺序队假溢出

可以看出整个队列整体后移,这样就出现了假溢出现象,而实际上队列并没有放满。解决假溢出的方法之一是将队列的数据区看成是头尾相接的循环结构,头尾指针的关系不变。如图所示:

则入队时队尾指针加1的操作应修改为:
sq->rear = (sq->rear+1)%MAXSIZE
出队时队头指针加1的操作应修改为:
sq->front = (sq->front+1)%MAXSIZE

使用此方法需要注意的是,顺序队列在判断数组是否已满时,出现下面情况:
-
当队列为空时,队列的头指针等于队列的尾指针;
-
当数组满员时,队列的头指针等于队列的尾指针;
为了对其进行区分,最简单的解决办法是:牺牲掉数组中的一个存储空间,判断数组满员的条件是:尾指针的下一个位置和头指针相遇,就说明数组满了。
顺序队的操作实现
顺序队定义
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */
typedef int Status;
typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */
/* 循环队列的顺序存储结构 */
typedef struct
{
QElemType data[MAXSIZE];
int front; /* 头指针 */
int rear; /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}SqQueue;
创建空队列
//初始化一个空队列Q
Status InitQueue(SqQueue *Q){
Q->front = 0;
Q->rear = 0;
return OK;
}
清空队列
//将队列清空
Status ClearQueue(SqQueue *Q){
Q->front = Q->rear = 0;
return OK;
}
队列判空
//若队列Q为空队列,则返回TRUR,否则返回FALSE;
Status QueueEmpty(SqQueue Q){
//队空标记
if (Q.front == Q.rear)
return TRUE;
else
return FALSE;
}
获取队列长度
//返回Q的元素个数,也就是队列的当前长度
int QueueLength(SqQueue Q){
return (Q.rear - Q.front + MAXSIZE)%MAXSIZE;
}
入队列
//若队列未满,则插入元素e为新队尾元素
Status EnQueue(SqQueue *Q,QElemType e){
//队列已满
if((Q->rear+1)%MAXSIZE == Q->front)
return ERROR;
//将元素e赋值给队尾
Q->data[Q->rear] = e;
//rear指针向后移动一位,若到最后则转到数组头部;
Q->rear = (Q->rear+1)%MAXSIZE;
return OK;
}
出队列
//若队列不空,则删除Q中队头的元素,用e返回值
Status DeQueue(SqQueue *Q,QElemType *e){
//判断队列是否为空
if (Q->front == Q->rear) {
return ERROR;
}
//将队头元素赋值给e
*e = Q->data[Q->front];
//front 指针向后移动一位,若到最后则转到数组头部
Q->front = (Q->front+1)%MAXSIZE;
return OK;
}
链队列
链式存储的队称为链队列。和链栈类似,链队列可以用单链表来实现,根据队的FIFO原则,为了操作上的方便,可以分别设置一个头指针和一个尾指针。

链队列的操作实现
链队列定义
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */
typedef struct QNode /* 结点结构 */
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct /* 队列的链表结构 */
{
QueuePtr front,rear; /* 队头、队尾指针 */
}LinkQueue;
创建空队列
/*初始化队列*/
Status InitQueue(LinkQueue *Q){
//1\. 头/尾指针都指向新生成的结点
Q->front = Q->rear = (QueuePtr)malloc(sizeof(QNode));
//2.判断是否创建新结点成功与否
if (!Q->front) {
return ERROR;
}
//3.头结点的指针域置空
Q->front->next = NULL;
return OK;
}
清空队列
/*将队列Q置空*/
Status ClearQueue(LinkQueue *Q){
QueuePtr p,q;
Q->rear = Q->front;
p = Q->front->next;
Q->front->next = NULL;
while (p) {
q = p;
p = p->next;
free(q);
}
return OK;
}
判空队列
/*判断队列Q是否为空*/
Status QueueEmpty(LinkQueue Q){
if (Q.front == Q.rear)
return TRUE;
else
return FALSE;
}
获取队列长度
/*获取队列长度*/
int QueueLength(LinkQueue Q){
int i= 0;
QueuePtr p;
p = Q.front;
while (Q.rear != p) {
i++;
p = p->next;
}
return i;
}
入队列
/*插入元素e为队列Q的新元素*/
Status EnQueue(LinkQueue *Q,QElemType e){
//为入队元素分配结点空间,用指针s指向;
QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
//判断是否分配成功
if (!s) {
return ERROR;
}
//将新结点s指定数据域.
s->data = e;
s->next = NULL;
//将新结点插入到队尾
Q->rear->next = s;
//修改队尾指针
Q->rear = s;
return OK;
}
出队列
/*出队列*/
Status DeQueue(LinkQueue *Q,QElemType *e){
QueuePtr p;
//判断队列是否为空;
if (Q->front == Q->rear) {
return ERROR;
}
//将要删除的队头结点暂时存储在p
p = Q->front->next;
//将要删除的队头结点的值赋值给e
*e = p->data;
//将原队列头结点的后继p->next 赋值给头结点后继
Q->front->next = p ->next;
//若队头就是队尾,则删除后将rear指向头结点.
if(Q->rear == p) Q->rear = Q->front;
free(p);
return OK;
}