队列
队列的概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
队列的实现
队列可以数组也可以链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低(如果数组要出数据,那么就要挪动数据)。
下面我们用链表的方式实现队列
1.初始化队列
1).创建链表节点
链表需要有节点,节点与节点之间进行链接,节点中包含数据与下一个结点的地址。
typedef int QDataType;// 插入数据的类型
typedef struct QueueNode
{
struct QueueNode* next;// 指针域
QDataType data;// 数据域
}QueueNode;
2).链表式队列与单链表有所不同,后者只需要头指针指向链表第一个节点即可(不需要尾指针,虽然可以有尾指针,但是作用不是很明显);前者需要两个指针,一个是头指针,一个是尾指针,两个变量以上,就可以用结构体的形式,把两个指针包装起来了,这样更方便使用,不用结构体也可以。两个指针相当于出口和入口的关系。队列由两个指针控制了。
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
}Queue;
3).创建初始化函数
对刚刚创建的结构体Queue进行初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
2.销毁队列
队列中的每一个结点所占用的内存空间都是动态开辟的,当我们使用完队列后需要及时释放队列中的每一个结点。
//队列销毁
//销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);
QListNode* cur = pq->head;//接收队头
//遍历链表,逐个释放结点
while (cur)
{
QListNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = NULL;//队头置空
pq->tail = NULL;//队尾置空
}
3.检测队列是否为空
检测队列是否为空,即判断队头指针指向的内容是否为空。
//判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL && pq->tail==NULL;
}
4.队尾入队列
入队列,即申请一个新结点并将其链接到队尾,然后改变队尾的指针指向即可。需要注意的是:若队列中原本无数据,那么我们只需让队头和队尾均指向这个新申请的结点即可。
// 进队列
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
printf("fail QueuePush ");
exit(-1);
}
//新节点初始化
newnode->data = x;
newnode->next = NULL;
if (pq->head == NULL)// 无结点链表
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;// 有结点链表
pq->tail = newnode;
}
}
5.出队列
出队列只能从对头开始出,这样才符合队列的特性,
出队列,即释放队头指针指向的结点并改变队头指针的指向即可。
若队列中只有一个结点,那么直接将该结点释放,然后将队头和队尾置空即可。
// 出队列
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));//检查队列是否为空,如果是则断言
QueueNode*next = pq->head->next;// 记录下一个结点
free(pq->head);// 释放头节点空间
pq->head = next;// 头指针指向前一个节点
if (pq->head == NULL)// tail 指向的空间被释放了,但是tail还是指向那个空间,造成非法访问
//预防野指针
if (pq->head == NULL)
{
pq ->tail = NULL;
}
}
6.获取队列头部元素
获取队列头部元素,即返回队头指针指向的数据即可。
// 取对头的数据
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
7.获取队列尾部元素
//取队尾的数据
QDataType QueueBack(Queue * pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
};
8.获取队列中有效元素个数
队列中有效元素个数,即队列中的结点个数。
我们只需遍历队列,统计队列中的结点数并返回即可。
//队列的大小
int QueueSize(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
int count = 0;
QueueNode* cur = pq->head;
while (cur)
{
count++;
cur = cur->next;
}
return count;
}