目录
检查队列是否为空
队列的概念及其结构
只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
FIFO(First In First Out) 。
入队列:进行插入操作的一端称为队尾; 出队列:进行删除操作的一端称为队头。
其实队列跟我们现实生活中的排队差不多。你要进入这个队伍,你是不是要从队尾排,出去是不是从队头处。也就是我们在餐厅买饭,在队尾排队(队列的插入),轮到你的时候,你也就是这个队的队头了,然后打好饭就直接从队头走了(队列的删除操作)。
队列的实现
队列也可以用数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,也就是当删除数据时,需要挪动数据,效率会比较低。因此我们可以优先选择单链表(无头单向非循环链表)实现队列。
为了提高效率,我们可以分别定义一个头指针phead、尾指针ptail。让phead指向单链表(队列)的头结点,让ptail指向单链表(队列)的尾结点。
队列需要定义两个结构体。第一个要定义结点结构,即是每一个结点。定义的结点结构包含结构体的指针域和数据域。
第二个结构体是将头指针phead和尾指针ptail,和队列的成员数量size封装起来,定义在结构体中。定义的第二个结构体其实就是为了能够提高插入删除的效率,不用穿太多的参数。phead、ptail是结构体的成员,是结构体成员内部,改变的是结构体。
typedef int SQDataType;
//定义结点结构
typedef struct QueueNode
{
SQDataType data;//存储节点的数据
struct QueueNode* next;//存储节点的地址
}QNode;
//将头指针phead和尾指针ptail封装起来
typedef struct Queue
{
QNode* phead;//链表头指针
QNode* ptail;//链表尾指针
int size;//数量
}Queue;
队列的初始化
首先就是队列的初始化,让队列为空。
void SQInit(Queue* pq)
{
assert(pq);//传个结构体指针过来,结构体外面肯定已经开辟好,所以不为空
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;//成员数量初始化为0
}
队列的插入操作(队尾入队列)
要在一个队列中插入一个数据,首先要开辟一个新结点newnode,让newnode的数据域存储需要插入的数据,因为是在队列的队尾插入,所以newnode会成为新的队尾,即指针域置为空。因为在队列中新插入了一个结点,所以队列中的成员数量会加一(size++)
插入分两种情况:一是:插入的队列为空。二是:插入的队列不为空。
当插入的队列为空时:头指针和尾指针会指向队列内的唯一一个结点,即指向同一个结点。
pq->phead = pq->ptail = newnode;最后队列的成员数量加一
当插入的队列不为空时:pq->ptail->next会线连接上newnode,即插入的newnode会成为新的尾结点,pq->ptail指向新的尾结点(pq->ptail = newnode),队列的成员数量加一
void SQPush(Queue* pq, SQDataType x)
{
assert(pq);
//开辟一个新节点newnode,newnode的数据域就是需要插入的x,指针域为空,因为是尾结点
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;//newnode的数据域为x
newnode->next = NULL;//newnode的指针域为空
//队列为空时
if (pq->phead == NULL)
{
assert(pq->ptail == NULL);
pq->phead = pq->ptail = newnode;
}
else
//队列不为空时
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;//出入一个节点,成员数量+1
}
队列的删除操作(队头出队列 )
首先要进行队列的删除操作时,队列不能为空。其次要判断队列中有一个结点还是多个结点。
情况一:队列中只有一个结点时 ,pq->phead->next = NULL;释放掉这一个节点,队列就为空了,pq->phead = pq->ptail = NULL;成员数量减一(size--)。
情况二:队列中有多个节点时,释放掉头结点,头指针会指向头结点的下一个位置,成为新的头结点。成员数量减一(size--)。
void SQPop(Queue* pq)
{
assert(pq);
assert(!SQEmpty(pq));//队列为空不能删
//队列只有一个结点时
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
//队列有多个结点时
{
QNode* next = pq->phead->next;//记录头结点的下一个位置
free(pq->phead);//释放掉头结点
pq->phead = next;//头指针指向下一个节点,即next成为新的头结点
}
pq->size--;//成员数量-1
}
获取队头元素
队列不能为空,直接返回队列的第一个元素就可以了,非常简单。
SQDataType SQFront(Queue* pq)
{
assert(pq);
assert(!SQEmpty(pq));//队列不能为空
return pq->phead->data;//返回队头数据
}
获取队尾元素
队列不能为空,直接返回队列的最后一个元素就可以了,非常简单。
SQDataType SQBack(Queue* pq)
{
assert(pq);
assert(!SQEmpty(pq));//队列不能为空
return pq->ptail->data;//返回队尾数据
}
获取队列中有效元素的个数
直接返回元素个数就可以了,也就是size
int SQSize(Queue* pq)
{
assert(pq);
return pq->size;
}
检查队列是否为空
直接判断size == 0,就可以了。也可以判断头指针和尾指针是不是都是指向空。
布尔类型,为空返回true,不为空返回false。
bool SQEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}