一.队列
1.队列的概念及结构
队列是只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特点,即:先进队列的先出来,后进队列的后出来。我们称进行插入操作的一端为队尾,进行删除操作的一段=端为队头。在结构上,队列与平时餐厅的叫号系统相似。当餐厅需要排队时,我们通常会进行排号,先排队的号,会被先叫,后排队的号会被后叫。
2.队列的实现
首先我们需要考虑队列需要基于什么结构来实现,数组,单链表还是双链表。首先我们就可以排除的是数组。因为在队列中我们需要去进行队尾插入和队头删除,在应用数组时我们会发现队尾插入我们需要遍历数组才能得出插入的位置,队头删除数据后会需要将所有数组中的数据都向前挪动一位,所以不能应用数组来实现队列。那么我们也可以想到双向链表,毕竟双链表在找头尾节点都很容易,且删除插入数据时只需要改变指针的指向就可以了。但是当我们把单链表的尾节点存储我们会发现单链表与双链表一样好用,且单链表的指针数目少更加的节省空间。所以我们选择基于单链表来实现队列。
1.队列的创建
队列基于单链表实现,所以应用单链表实现队列的基本结构。
typedef int QDataType;
typedef struct QueueNode
{
QDataType val;
struct QueueNode* next;
}QNode;
2.队列的头尾指针封存
当我们在队列中插入删除数据时,因为实参形参的问题所以我们需要传递二级指针,但是传递二级指针太过麻烦,所以这里介绍一个新的方法。因为我们每次传递时都需要传递头指针和尾指针,有多个数据需要传递,所以我们可以将头尾指针封存在一个结构体里面,每次传递时就只需要传递结构体的指针就可以了,就不用传递数据的指针的地址了,可以避免用二级指针,传过多的参数,这样就方便了很多。为了便于统计队列中的数据个数,我们可以在结构体中再加一个元素size用来统计个数。
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
3.队列的初始化
初始化队列只需要将两个指针置为空,size设为0就可以了。
//队列的初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
4.队列的插入数据
在队列中插入数据首先需要扩容,接着再将新建的节点与原队列使用指针相连即可。最后不要忘了将size的个数加1。
//队列的插入
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(1);
}
newnode->next = NULL;
newnode->val = x;
if (pq->ptail == NULL)
{
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
5.队列的删除数据
在队列中删除数据共分两种情况,一是队列中还有多个数据,那么我们只需要将头节点的下一个节点存储,再将头节点释放,并将下一个节点作为新的头节点。第二种是,当队列中只剩下一个数据,那么当我们只释放掉phead后我们会发现,虽然phead指向了空,但是由于ptail与phead指向同一个数据释放掉phead的同时我们也将ptail释放了,所以ptail现在是野指针,所以我们需要将ptail手动置为空。
//队列的删除
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->size != 0);
//方法一:后判是否为只剩一个节点
//QNode* next = pq->phead->next;
//free(pq->phead);
//pq->phead = next;
//if (pq->phead == NULL)
//{
// pq->ptail = NULL;
//}
//pq->size--;
//方法二:先判是否为只剩一个节点
if (pq->phead->next == NULL)
{
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
else
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
pq->size--;
}
}
6.统计队列内的数据个数
由于我们在结构体中已经有了队列数据个数的计算所以我们只需要返回size就可以了。
//统计队列内的数据个数
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
7.获取队列的头数据
//获取队列的头数据
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
8.获取队列的尾数据
//获取队列的尾数据
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->ptail);
return pq->ptail->val;
}
9.队列的判空
当Queue中的size为0时队列为空。
//队列的判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
10.队列的销毁
//队列的销毁
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* pcur = pq->phead;
while (pcur)
{
QNode* next = pcur->next;
free(pcur);
pcur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
队列的基本实现方法就讲完啦,大家有兴趣的自己试着敲一敲哦~