1.什么是队列?
只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)的特点 。
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
队列的逻辑图:
2.实现队列的底层结构选择
1.数组:
数组实现队列的逻辑图:
用数组来实现队列,入队列就直接尾插,但是队头是0号下标的位置,每次出队列的时候都要挪动数据,这样大大减少了效率,不推荐使用数组来实现队列。
2.链表:
链表实现队列的逻辑图:
用链表来实现栈,对头位置是头节点,出队列就头删,队尾是尾节点,入队列就尾插。唯一不好的是,入队列就尾插的时候要找尾,这样也会减少效率,所以我们在定义队列节点的时候多加上一个指针来存储尾节点的指针,这样就入队列就基本上没有时间消耗了,所以推荐使用链表来实现队列,简单高效。
3.队列的实现
实现队列需要用到链表,不会链表的同学可以看这篇博客------->C语言数据结构:链表
队列只是在结构上复杂,实现起来反而比链表简单。
1.队列的定义
typedef int QDataType;
//链式结构:表示队列
typedef struct QListNode
{
struct QListNode* _pNext;
QDataType _data;
}QNode;
//队列的结构
typedef struct Queue
{
QNode* _front;
QNode* _rear;
int size;
}Queue;
第一个结构体是链表,用来存储数据,第二个结构体就是队列的结构了,第一个指针是头节点的指针,第二个指针是尾节点的指针,size用来记录队列的数据个数。
2.队列的初始化
哨兵位头节点可要可不要,我选择不用哨兵位头节点,所以初始化的时候头节点的指针和尾节点的指针直接置空,将记录有效数据个数size置0。
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);
q->_front = NULL;
q->_rear = NULL;
q->size = 0;
}
3.入队列
在堆上申请的空间是用来存储数据的链表节点(Qnode),而不是队列结构(Queue),检查在堆上开辟空间是否成功,入队列需要考虑的两个点:
1.size为0(无数据):需要将头节点和尾节点的指针指向新开辟的节点;
2.size大于0(有数据):直接尾插,尾插完后要更新尾节点的指针;
最后更新size,入队列完成。
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
assert(q);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if(newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->_data = data;
newnode->_pNext = NULL;
if(q->size == 0)//无数据
{
q->_front = newnode;
q->_rear = newnode;
}
else//有数据,尾插
{
q->_rear->_pNext = newnode;
q->_rear = newnode;
}
q->size++;//更新数据的有效个数
}
4.出队列
先断言一下size(数据的有效个数),防止别人没有数据时还进行出队列的操作。更新头节点的指针,头删完成后更新一下size,出队列完成。
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);
assert(q->size > 0);
QNode* del = q->_front;
q->_front = del->_pNext;
free(del);
q->size--;
}
5.获取队列头部元素
直接返回头节点指针所指向节点的数据。
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(q->size > 0);//检查是否有数据
return q->_front->_data;
}
6.获取队列队尾元素
直接返回尾节点指针所指向节点的数据。
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
assert(q);
assert(q->size > 0);//检查是否有数据
return q->_rear->_data;
}
7.获取队列中有效元素个数
直接返回size,size记录的是数据的有效个数,入队列和出队列都会更新size。
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);
return q->size;
}
8.检测队列是否为空
检测size,size为0返回真,否则返回假。
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q)
{
assert(q);
return q->size == 0;
}
9.销毁队列
复用出队列这个函数,最后将头节点和尾节点的指针置空,销毁队列完成。
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
while(q->size)
{
QueuePop(q);
}
q->_front = NULL;
q->_rear = NULL;
q->size = 0;//可以不写
}