什么是队列?队列有什么样的特性?它的应用场景有哪些?
本文会对队列这种数据结构进行进行庖丁解牛般的讲解,让你彻底学会数据结构!
队列是一种常见的数据结构,它按照先进先出(FIFO)的原则进行操作。队列中的元素按照进入的顺序排列,新元素插入到队列的一端,称为队尾,已有元素的删除操作则发生在队列的另一端,称为队头。
这个可以简单理解成,就像是我们生活中的食堂打饭排队一样,先来的在前面后来的在后面,前面的打完饭后就走了。这就像是数据结构中的队列。
本文我们的队列使用链表的形式来进行队列的实现。这里更推荐链表实现起来不会那么复杂。
typedef int QDatatype;
typedef struct Qlistnode
{
struct Qlistnode* next;
QDatatype data;
}Qlistnode;
typedef struct Queue
{
Qlistnode* fornt;
Qlistnode* rear;
int size;
}Queue;
对数据类型进行重命名,这样以后需要更换其他数据类型使用的时候只需要更改这一个地方就可以了。
这里有两个结构体,进行了嵌套。定义一个链表的结点,包含当前结点元素和指向下一个结点的指针。然后定义一个队列结构体,队列中两个结构体体指针分别代表队头和队尾,size是当前队列的有效元素个数。
这样做的目的是,方便了队列的头删(出队列)和尾插(入队列),已经获取队列内的元素个数和队尾、队头的元素。
void QueueInit(Queue* q)
{
assert(q);
q->fornt = NULL;
q->rear = NULL;
q->size = 0;
}
这里先将所以的指针都置空,size为0,因为是初始化所以队中无元素。
在队列插入数据,要先开辟一个结点的空间,用来存放值和下一个结点的地址。这里要进行两种情况的判断:如果队头为空时,代表此时队列中无元素,那么队头和队尾指针指向同一块空间。当队头不为空时,就将队尾的指针指向新开辟的结点。插入新数据后,size的个数++。
void QueuePush(Queue* q, QDatatype x)
{
assert(q);
Qlistnode* newnode = (Qlistnode*)malloc(sizeof(Qlistnode));
if (newnode == NULL)
{
perror("malloc");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (q->rear == NULL)
{
q->fornt = q->rear = newnode;
}
else
{
q->rear->next = newnode;
q->rear = newnode;
}
q->size++;
}
在队头删除数据,此处和入队列一样,要进行两种情况的判断:
如果队头和队尾指针同时指向一块空间时,此时队列中只有一个元素,所以释放队头或队尾指针都可,然后将队头和队尾指针置空,方便下一次进行插入数据(入队列)。
队头和队尾指针不相等时,表名队列有最少一个以上的元素,创建一个临时结点用来存放队头指针下一个元素的地址,然后释放队头指针,再让队头指针指向下一个元素。
出队列后,队列中的有效元素个数就少了一个,所以size也要进行–。
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
if (q->fornt == q->rear)
{
free(q->fornt);
q->fornt = NULL;
q->rear = NULL;
}
else
{
Qlistnode* newnode = q->fornt->next;
free(q->fornt);
q->fornt = newnode;
}
q->size--;
}
要获取元素前需要进行判空,如果队列是空,那么就会报错。
队头指针指向的就是队列的首元素地址,进行解引用就可以获取队头的元素。
QDatatype QueueFornt(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->fornt->data;
}
要获取元素前需要进行判空,如果队列是空,那么就会报错。
队头指针指向的就是队尾的首元素地址,进行解引用就可以获取队尾的元素。
QDatatype QueueRear(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->rear->data;
}
队列如果为空的情况下代表队头指针是空,此时队列无元素。
bool QueueEmpty(Queue* q)
{
assert(q);
return q->fornt == NULL;
}
我们先前定义的size就是队列中有效元素的个数。
int QueueSize(Queue* q)
{
assert(q);
return q->size;
}
因为队列是链表实现的,所以这里的释放空间要写成循环,释放队列中每一个结点的空间。
创建临时指针,让newnode去迭代。最后队头和队尾指针都置空,size归0。
void QueueDestroy(Queue* q)
{
assert(q);
Qlistnode* newnode = q->fornt;
while (newnode)
{
q->fornt = newnode->next;
free(newnode);
newnode = q->fornt;
}
q->fornt = q->rear = NULL;
q->size = 0;
}
队列在计算机科学和软件开发中有广泛的应用场景:
以上只是一些常见的应用场景,队列还可以用于解决其他问题,如数据流量控制、请求排队、打印队列等。队列的先进先出特性使其在这些场景下能够提供高效的数据处理和调度能力。
本篇对队列这种数据结构进行了概念的说明,对队列的实现细致入微,最后普及了队列这种数据结构的泛用性!
☁️ 好啦,由于篇幅有限,本章仅对队列进行了细致入微的讲解,后序还会有更多的数据结构文章分享哦!
看到这里了希望给博主留个: 点赞收藏 ⭐️ 关注!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。