今天我们学习的是数据结构里面的队列,目录放在下方供大家参考和学习,如下:
一、队列的概念
概念:队列只允许在一端进行放入数据,另一端输出数据操作的特殊线性表,队列具有数据先进先出的特点,被称为(First In First Out),也可以被简化为(FIFO),进行数据放入操作的一端称为队列的尾部,输出数据的一端被称为队列的头部。这些概念枯燥有难以理解,所以大家结合下面队列的结构及操作原理来理解更好一点。
二、队列的结构及操作原理
下面我们来看一看队列的结构图,方便我们来理解:
再下来我们看看队列的操作原理,我用放入数据和输出数据的图来让大家更加明白对队列的操作原理,先看放入数据的操作,如下:
接下来是输出数据的操作,请看下图:
看了队列的结构和操作原理相信大家已经明白了队列的先进先出的原理了,那么接下来咱们继续往后看。
三、队列的底层逻辑和实现
1、在实现队列之前我们应该明白它的底层逻辑和如何实现,要实现一个先进先出的操作,如果我们用数组的底层逻辑实现会怎样,请看下图:
所以在这里我们不采取用数组为底层逻辑来实现队列,接下来我们看看用链表来实现如何:
如上图,所以我们用单链表来实现队列,利用单链表的尾插来实现队列的放入数据,利用队列的头删来实现队列的数据输出,所以在这里可以理解为队列的底层逻辑是单链表,用单链表很简单可以实现。接下来我们来实现一个队列吧。
2、队列的实现
我们先来看看队列的结构实现代码,请结合代码中的注释去理解:
typedef int QDataType;
//队列的结构(单链表底层逻辑)
typedef struct QueueNode
{
QDataType val;
struct QueueNode* next;
}QueueNode;
//为了更好的实现队列的数据输出和数据的输入操作,所以我们引用两个指针来维护队列的这些操作
//我们把这两个指针放到一个结构体中,这样可以不用使用双指针来操作,操作起来也会比较简单
//队列的头尾指针,用来获取队列的头尾数据
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
int size; // 用来记录队列中的数据个数
}Queue;
接下来我们看看要实现队列所需要的各种函数
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
下面就是队列实现时每个函数的实现,请看下面代码和注释:
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);
q->head = NULL;
q->tail = NULL;
q->size = 0;
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
assert(q);
//在放入数据的时候需要先开辟一块QueueNode的空间才可以放入数据,因为我们Queue结构体中只是QueueNode结构体指针,不能直接放入数据
QueueNode* tem = (QueueNode*)malloc(sizeof(QueueNode));
if (tem == NULL)
{
perror("malloc");
exit(1);
}
//队列的放入数据要用到单链表的尾插操作
if (q->head == NULL)
{
//如果单链表为空,也就是第一次放入数据
q->head = q->tail = tem;
q->head->val = data;
q->tail->next = NULL;
q->size++;
}
else
{
//单链表不为空,也就不是第一次放入数据
//头节点不变,尾节点继续往后走
q->tail->next = tem;
q->tail = q->tail->next;
q->tail->val = data;
q->tail->next = NULL;
q->size++;
}
}
// 队头出队列
void QueuePop(Queue* q)
{
assert(q);
//要输出队列的头数据,需要先记住头节点之后的节点,然后在释放头节点,置空即可
//但是如果队列中只有一个数据,那么tail节点就会成为野指针,也就不会被找到和释放,所以我们需要分情况
if (q->head == q->tail)
{
//说明队列中只有一个数据
free(q->head);
q->head = q->tail = NULL;
q->size = 0;
}
else
{
//队列中不止一个数据
QueueNode* tem = q->head->next;
free(q->head);
q->head = tem;
q->size--;
}
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(q);
assert(q->head);
return q->head->val;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
assert(q);
assert(q->head);
return q->tail->val;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);
return q->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* q)
{
assert(q);
if (q->head == NULL)
{
return true;
}
return false;
}
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QueueNode* cur = q->head;
while (cur)
{
QueueNode* tem = cur->next;
free(cur);
cur = tem;
}
}
好了,今天的内容就到这里,大家如果还有什么问题可以在评论区留言或者私信我,我来给大家解答!!!