一。概念
队列 是仅限在 一端 进行 插入,另一端 进行 删除 的 线性表。
队列 又被称为 先进先出 (First In First Out) 的线性表,简称 FIFO 。
进行元素删除的一端为队首
插入的一段为队尾
二。队列的顺序表实现
1.数据结构实现
struct queue{
Datatype data[maxn];
int head,tail;
}
2.入对
void QueueEnqueue(struct Queue *que, DataType dt) { // (1)
que->data[ que->tail ] = dt; // (2)
que->tail = que->tail + 1; // (3)
}
3.出队
que->head++;
如图所示,橙色元素 为原先的 队首元素,执行 出队 操作以后,黃色元素 成为当前的 队首元素,执行完毕以后,队首指针加一。由于是顺序表实现,队首元素前面的那些元素已经变成无效的了,具体来看下代码实现
4.清空
que->head=que->tail=0;
5.其他
DataType QueueGetFront(struct Queue* que) {
return que->data[ que->head ]; // (1)
}
int QueueGetSize(struct Queue* que) {
return que->tail - que->head; // (2)
}
int QueueIsEmpty(struct Queue* que) {
return !QueueGetSize(que); // (3)
}
三。链表实现
1.数据结构定义
struct queuenode;
struct queuenode{
int data;
struct queuenode *next;
}
struct queue{
struct queuenode*head,tail;
int size;
}
2.入队(类似尾插法)
void queueinsert(struct queue*que,int dt)
{
struct queuenode*insertnode=(struct queuenode*)malloc(sizeof(struct queuenode));
queuenode->data=dt;
queuenode->next=NULL;
if(que->tail)//如果队尾不为空
{
que->tail->next=insertnode;
que->tail=insertnode;
}
else{
que->tail=que->head=insertnode;
}
++que->size;
}
3.出队(即删除头结点)
void dequeue(struct queue*que)
{
struct queuenode*temp=que->head;
que->head=que->next;
free(temp);
--que->size;
if(!que->size)//如果此次出队之后队列为空,那么出队之前head=tail=temp
//上述操作将head置空,因此还要将tail置空
que->tail=NULL;
}
}
4.清空
清空队列 可以理解为:不断的 出队,直到 队列元素 个数为零为止。由于链表结点是动态申请的内存,所以在没有其它结点引用时,是需要释放内存的,不像数组那样直接将 队首指针 和 队尾指针 置空就行的。
void QueueClear(struct Queue* que) {
while(!QueueIsEmpty(que)) { // (1)
QueueDequeue(que); // (2)
}
}
5.其他
DataType QueueGetFront(struct Queue* que) {
return que->head->data; // (1)
}
int QueueGetSize(struct Queue* que) {
return que->size; // (2)
}
int QueueIsEmpty(struct Queue* que) {
return !QueueGetSize(que); // (3)
}
四。优缺点
1、顺序表实现
在利用顺序表实现队列时,入队 和 出队 的常数时间复杂度低,且 清空队列 操作相比 链表实现 能做到 O(1)O(1),唯一的不足之处是:需要预先申请好空间,而且当空间不够时,需要进行扩容,扩容方式本文未提及,可以参考以下文章:《C/C++ 面试 100 例》(四)vector 扩容策略。
当然,可以采用 循环队列,能够很大程度上避免扩容问题,但是当 入队速度 大于 出队速度 时,不免还是会遇到扩容的问题。
2、链表实现
在利用链表实现队列时,入队 和 出队 的常数时间复杂度略高,主要是每插入一个队列元素都需要申请空间,每删除一个队列元素都需要释放空间,且 清空队列 操作是 O(n)O(n) 的,直接将 队首指针 和 队尾指针 置空会导致内存泄漏。
好处就是:不需要预先分配空间,且在内存允许范围内,可以一直 入队,没有顺序表的限制。当然,链表的实现明显比数组实现要复杂,编码的时候容易出错。
需要注意的是,本文在讲解过程中,顺序表实现 的 队尾 和 链表实现 的 队尾 不是一个概念,顺序表实现的队尾没有实际元素值,而链表实现的则不然,请自行加以区分。