一、 队列的定义
队列简称队,它也是一种运算受限的线性表,其限制仅允许在表的一端进行插入,而在表的另一端进行删除。
把进行插入的一端称做队尾(rear),进行删除的一端称做队首或队头(front)。
向队列中插入新元素称为进队或入队,新元素进队后就成为新的队尾元素;从队列中删除元素称为出队或离队,元素出队后,其后继元素就成为队首元素。
由于队列的插入和删除操作分别是在各自的一端进行的,每个元素必然按照进入的次序出队,所以又把队列称为先进先出表。
队列的基本运算如下:
InitQueue(&q):初始化队列。构造一个空队列q。
DestroyQueue(&q):销毁队列。释放队列q占用的存储空间。
QueueEmpty(q):判断队列是否为空。若队列q为空,则返回真;否则返回假。
enQueue(&q,e):进队列。将元素e进队作为队尾元素。
deQueue(&q,&e):出队列。从队列q中出队一个元素,并将其值赋给e。
二、队列的顺序存储结构及其基本运算的实现
假设队列的元素个数最大不超过整数MaxSize,所有的元素都具有同一数据类型ElemType,则顺序队列类型SqQueue定义如下:
typedef struct
{ ElemType data[MaxSize];
int front,rear;//队首和队尾指针
} SqQueue;
1)顺序队的四要素(初始时front=rear=-1)
队空条件:front=rear
队满条件:rear=MaxSize-1
元素e进队:rear++;data[rear]=e;
元素e出队:front++;e=data[front];
2) 顺序队中实现队列的基本运算
(1)初始化队列InitQueue(q)
构造一个空队列q。将front和rear指针均设置成初始状态即-1值。
void InitQueue(SqQueue *&q)
{ q=(SqQueue *)malloc (sizeof(SqQueue));
q->front=q->rear=-1;
}
(2)销毁队列DestroyQueue(q)
释放队列q占用的存储空间。
void DestroyQueue(SqQueue *&q)
{
free(q);
}
(3)判断队列是否为空QueueEmpty(q)
若队列q满足q->front==q->rear条件,则返回true;否则返回false。
bool QueueEmpty(SqQueue *q)
{
return(q->front==q->rear);
}
(4)进队列enQueue(q,e)
在队列不满的条件下,先将队尾指针rear循环增1,然后将元素添加到该位置。
bool enQueue(SqQueue *&q,ElemType e)
{ if (q->rear==MaxSize-1) //队满上溢出
return false;
q->rear++;
q->data[q->rear]=e;
return true;
}
在队列q不为空的条件下,将队首指针front循环增1,并将该位置的元素值赋给e。
bool deQueue(SqQueue *&q,ElemType &e)
{ if (q->front==q->rear) //队空下溢出
return false;
q->front++;
e=q->data[q->front];
return true;
}
3)环形队中实现队列的基本运算
用rear==MaxSize-1作为队满的条件有缺陷。可能队列为空,但仍满足该条件。这时进队时出现“上溢出”,这种溢出并不是真正的溢出,在data数组中存在可以存放元素的空位置,所以这是一种假溢出。
为了能够充分地使用数组中的存储空间,把数组的前端和后端连接起来,形成一个环形的顺序表,即把存储队列元素的表从逻辑上看成一个环,称为环形队列或循环队列。
环形队列的四要素:
队空条件:front==rear
队满条件:(rear+1)%MaxSize=front
进队e操作:rear=(rear+1)%MaxSize; 将e放在rear处
出队操作:front=(front+1)%MaxSize;取出front处元素e;
(1)初始化队列InitQueue(q)
构造一个空队列q。将front和rear指针均设置成初始状态即0值。
void InitQueue(SqQueue *&q)
{ q=(SqQueue *)malloc (sizeof(SqQueue));
q->front=q->rear=0;
}
(2)进队列enQueue(q,e)
在队列不满的条件下,先将队尾指针rear循环增1,然后将元素添加到该位置。
bool enQueue(SqQueue *&q,ElemType e)
{ if ((q->rear+1)%MaxSize==q->front) //队满上溢出
return false;
q->rear=(q->rear+1)%MaxSize;
q->data[q->rear]=e;
return true;
}
(3)出队列deQueue(q,e)
在队列q不为空的条件下,将队首指针front循环增1,并将该位置的元素值赋给e。
bool deQueue(SqQueue *&q,ElemType &e)
{ if (q->front==q->rear) //队空下溢出
return false;
q->front=(q->front+1)%MaxSize;
e=q->data[q->front];
return true;
}
三、 队列的链式存储结构及其基本运算的实现
链队组成:
(1)存储队列元素的单链表
(2) 指向队头和队尾指针的链队头节点
单链表中数据节点类型QNode定义如下:
typedef struct qnode
{ ElemType data; //数据元素
struct qnode *next;
} QNode;
链队中头节点类型LiQueue定义如下:
typedef struct
{ QNode *front; //指向单链表队头节点
QNode *rear; //指向单链表队尾节点
} LiQueue;
链队的4要素:
队空条件:front=rear=NULL
队满条件:不考虑
进队e操作:将包含e的节点插入到单链表表尾
出队操作:删除单链表首数据节点
在链队存储中,队列的基本运算算法如下。
(1)初始化队列InitQueue(q)
构造一个空队列,即只创建一个链队头节点,其front和rear域均置为NULL,不创建数据元素节点。对应算法如下:
void InitQueue(LiQueue *&q)
{ q=(LiQueue *)malloc(sizeof(LiQueue));
q->front=q->rear=NULL;
}
(2)销毁队列DestroyQueue(q)
释放队列占用的存储空间,包括链队头节点和所有数据节点的存储空间。对应算法如下:
void DestroyQueue(LiQueue *&q)
{ QNode *p=q->front,*r; //p指向队头数据节点
if (p!=NULL) //释放数据节点占用空间
{ r=p->next;
while (r!=NULL)
{ free(p);
p=r;r=p->next;
}
}
free(p);
free(q); //释放链队节点占用空间
}
(3)判断队列是否为空QueueEmpty(q)
若链队节点的rear域值为NULL,表示队列为空,返回true;否则返回false。对应算法如下:
bool QueueEmpty(LiQueue *q)
{
return(q->rear==NULL);
}
(4) 入队enQueue(q,e)
创建data域为e的数据节点*s。
若原队列为空,则将链队节点的两个域均指向*s节点,否则将*s链到单链表的末尾,并让链队节点的rear域指向它。
void enQueue(LiQueue *&q,ElemType e)
{ QNode *p;
p=(QNode *)malloc(sizeof(QNode));
p->data=e;
p->next=NULL;
if (q->rear==NULL) //若链队为空,新节点是队首节点又是队尾节点
q->front=q->rear=p;
else
{ q->rear->next=p;//将*p节点链到队尾,并将rear指向它
q->rear=p;
}
}
(5)出队deQueue(q,e)
若原队列不为空,则将第一个数据节点的data域值赋给e,并删除之。
若出队之前队列中只有一个节点,则需将链队节点的两个域均置为NULL,表示队列已为空。对应的算法如下:
bool deQueue(LiQueue *&q,ElemType &e)
{ QNode *t;
if (q->rear==NULL) return false; //队列为空
t=q->front; //t指向第一个数据节点
if (q->front==q->rear) //队列中只有一个节点时
q->front=q->rear=NULL;
else //队列中有多个节点时
q->front=q->front->next;
e=t->data;
free(t);
return true;
}