队列
一、概念
队列也是一种特殊的线性表,其只允许在一端进行数据的插入操作,在另一端进行数据的删除操作。进行数据插入的一端称为队尾,进行数据删除的一端称为队头。其队列中的数据元素遵循先进先出(后进后出)原则。
数据的插入也叫做入队列,其操作是在队尾完成的。数据的删除也叫做出队列,其操作是在队头完成的。
二、特点
只允许在一端进行数据的插入(队尾),另一端进行数据的删除操作(队头),其队列中的数据元素遵循先进先出(后进后出)原则。
三、实现
3.1 队列实现体系结构的确立
对于队列的实现,采用顺序表和链表都可以进行实现,但其实现难度和复杂程度不同,例如,在进行队列中数据元素的删除操作中:
顺序表:
需要去挪动队头后的数据,所以其效率较低。
链表(主要采用了单链表):
只需要将头指针向后指向下一个即可。效率更高。
因此,主要采用单链表来进行队列的构建。
3.2 实现思路
对于队列的实现的话,可以定义两个链表结点,一个为头节点(head),一个为指向了最后一个元素结点(tail)。和一个记录总的元素个数的变量size。插入元素后,让tail往后走,删除元素时,让head往前走,具体细节看下面代码即可。
3.3 具体实现
3.2.1 结构体
typedef int QDataType;
typedef struct QListNode
{
QDataType data;
struct QListNode* next;
}QNode;//链表结点
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
实现时,使用了两个结构体,第一个结构体为链表结点信息,第二个结构体为队列信息。
3.2.2 队列的初始化
void QueueInit(Queue* q)
{
assert(q);
q->head = q->tail = NULL;
q->size = 0;
}
初始化内容很简单,让其队列内的信息初始化即可。对于assert()函数不清楚的,可看我上文对于栈的解析内容即可。
3.2.3 队列的插入
void QueuePush(Queue* q, QDataType x)
{
assert(q);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->next = NULL;
newnode->data = x;
if (q->head == NULL)
{
q->head = q->tail = newnode;
}
else
{
q->tail->next = newnode;
q->tail = newnode;
}
q->size++;
}
在插入时,要分情况进行插入,第一种也是特殊情况,也就是当插入第一个数据时,直接把新结点直接赋给head和tail结点即可。其余各种情况,都是单链表的插入操作。插入成功,size加1。
3.2.4 队列的删除操作
void QueuePop(Queue* q)
{
assert(q);
assert(q->head != NULL);
QNode* next = q->head->next;
if (next == NULL)
{
free(q->head);
q->head = q->tail = next;
}
else
{
free(q->head);
q->head = next;
}
q->size--;
}
在删除中,如果head结点为空,说明队列中没有元素,就不能进行删除了。另外,还要注意,当队列中只有一个元素进行删除时,删除后不仅要让head结点为空,还要注意也要让tail结点也为空。
删除成功,size减1。
3.2.5 获取队头元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(q->head != NULL);
return q->head->data;
}
只要队列中有元素,直接返回head结点所指向的内容即可。
3.2.6 获取队尾元素
QDataType QueueBack(Queue* q)
{
assert(q);
assert(q->tail != NULL);
return q->tail->data;
}
只要对列中有元素,直接返回tail所指向的内容即可。
3.2.7 获取队列总的元素个数
int QueueSize(Queue* q)
{
assert(q);
return q->size;
}
因其size记录的就为队列中总的元素个数,所以直接返回其size即可。
3.2.8 判断队列是否为空
bool QueueEmpty(Queue* q)
{
assert(q);
return q->head == NULL;
}
看head结点或tail结点是否为空即可。
3.2.9 队列的销毁
void QueueDestroy(Queue* q)
{
assert(q);
QNode* next = NULL;
while (q->head != NULL)
{
next = q->head->next;
free(q->head);
q->head = next;
}
q->tail = NULL;
q->size = 0;
}
对于队列的销毁,需要将所申请的所有内存都进行返回。因其需要将链表内所有非空结点都进行释放。再将size恢复初始值即可。
至此,队列讲解完毕。