hello 友友们~ 今天我们要开始学习队列啦~
话不多说,让我们开始吧!GO!
1.队列的概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。
队列具有
先进先出
FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
注意:队列的实现建议用单链表
2.队列的实现
2.1接口实现
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int QDataType;
//队列中的一个节点
typedef struct QueueNode
{
struct QueueNode* _next;// 指向下一个节点的指针
QDataType _data;// 存储的数据
}QueueNode;
//队列本身的定义
typedef struct Queue
{
QueueNode* _head;// 指向队列头部的指针
QueueNode* _tail;// 指向队列尾部的指针
}Queue;
//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestory(Queue* pq);
//队尾入队列
void QueuePush(Queue* pq, QDataType x);
//队头出队列
void QueuePop(Queue* pq);
//取队头数据(获取队列头部元素)
QDataType QueueFront(Queue* pq);
//取队尾数据(获取队列队尾元素)
QDataType QueueBack(Queue* pq);
// 检测队列是否为空
//返回1是空,返回0是非空
int QueueEmpty(Queue* pq);
// 获取队列中有效元素个数
int QueueSize(Queue* pq);
2.2初始化
//初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->_head = pq->_tail = NULL;
}
2.3销毁
//销毁
void QueueDestory(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->_head;//先保存头结点
while (cur)
{
QDataType* next = cur->_next;//先保存后一个结点位置
free(cur);//再释放
cur = next;//然后指针往后走
}
pq->_head = pq->_tail = NULL;
}
2.4队尾入队列
//队尾入队列
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
printf("内存不足\n");
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;//更新尾节点
}
}
重点代码分析:
newnode->_data = x;
:把要添加的元素x赋值给新节点的数据域_data
。newnode->_next = NULL;
:由于新节点是要添加到队列尾部的,所以将其_next指针置为NULL
。- 当
pq->_head
为NULL
时,表明队列为空。此时,新节点既是队列的头节点,也是队列的尾节点,所以将pq->_head
和pq->_tail
都指向新节点。
2.5队头出队列
//队头出队列
void QueuePop(Queue* pq)
{
assert(pq);
//队列为空时
if (pq->_head == NULL)
{
printf("队列为空,无法出队\n");
return;
}//这一段也可以写成assert(pq->head)
QueueNode* next = pq->_head->_next;//先保存下一个结点
free(pq->_head);
pq->_head = next;//更新头指针
if (pq->_head == NULL)
{
pq->_tail = NULL;
}
}
思考🤔🤔🤔:为什么要进行最后这次if判断
原因:🧐🧐🧐
在链式队列的出队操作中,当队列中只有一个元素时,移除这个元素后,队列就会变为空
。此时,pq->_head
会被置为NULL
。但原来的尾指针pq->_tail
仍然指向刚刚被移除的那个节点,而这个节点的内存已经被释放了。如果不将pq->_tail
也置为NULL
,pq->_tail
就会变成一个野指针
,后续对队列进行操作时可能会引发未定义行为。因此,需要通过这次判断来确保当队列变为空时,头指针和尾指针都被正确置为NULL
,以保证队列状态的正确性。
2.6获取队列头部元素
//取队头数据(获取队列头部元素)
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->_head);
return pq->_head->_data;
}
2.7获取队列队尾元素
//取队尾数据(获取队列队尾元素)
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->_tail );
return pq->_tail->_data;
}
2.8检测队列是否为空
// 检测队列是否为空
//返回1是空,返回0是非空
int QueueEmpty(Queue* pq)
{
assert(pq);
return pq->_head == NULL ? 1 : 0;//如果==NULL则返回1 反之返回0
}
2.9获取队列中有效元素个数
// 获取队列中有效元素个数
int QueueSize(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->_head;
int size = 0;
while (cur)
{
size++;
cur = cur->_next;
}
return size;
}
2.10函数调用
#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
void TestQueue()
{
Queue q;
//初始化
QueueInit(&q);
//入队列
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
//取数据
while (!QueueEmpty(&q))//这里要不为空所以加了一个!
{
printf("%d ", QueueFront(&q));//将队列头部元素的值打印输出
QueuePop(&q);//打印后删了
}
QueueDestory(&q);
printf("\n");
}
int main()
{
TestQueue();
return 0;
}
最后实现结果:
🎉🎉🎉
在这里本章就结束啦~
我们下期见~