本篇文章将带大家学习队列的相关知识点,并且讲解几道例题,请各位小伙伴耐心观看
队列的概念
遵循原则
先进先出
队列的实现
图解:
代码实现:
typedef int QDataType;
typedef struct QueuenNode
{
struct QueuenNode* next;
QDataType val;
}QNode;
typedef struct queue//这个结构体里存放的是next指针的头尾指针和计数器size
{
QNode* phead;
QNode* ptail;
int size;//计数器
}que;
void QueueInit(que* pq);//初始化
void QueuePush(que* pq, QDataType x);//入队
void QueuePop(que* pq);//出队
int QueueSize(que* pq);
QDataType QueueFront(que* pq);//从队头进
QDataType QueueBack(que* pq);//从队尾进
void QueeuDestroy(que* pq);//销毁
bool QueueEmpty(que* pq);//判空
这里我简单提一下初始化和销毁函数里的部分代码
初始化
为什么这里需要加一个assert(pq)?
首先pq的类型是que ,而que是表示含有头尾节点的结构体,因此要判空,若pq指针为空,则报错。那么下面的代码我就不做解释了,如有新来的小伙伴可以点击👇的链接观看上一篇文章的总结内容。
销毁
//视频待补充
上一篇文章的链接:http://t.csdnimg.cn/b5hPt(含初始化与销毁的思路模板)
入队
void QueuePush(que* 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;
当我们申请完空间后要将这一部分初始化,指针部分置为空即可,而val部分直接接收x的值即可
那么👇的部分其实比较简单就是用我们先前所学过的链表的知识点
if (pq->phead == NULL)
{
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;//链表的尾插
pq->ptail = newnode;//链表的尾插
}
pq->size++;
}
出队
void QueuePop(queue* pq)
{
assert(pq);
assert(pq->phead != 0);//头节点不为空
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
if (pq->phead == NULL)
{
pq->ptail = NULL;
}
pq->size--;
}
思路:首先判空,注意这里除了指针pq需要判空外,pq->phead也需要判空,因为当pq->head为空时,请问下面的语句还能执行吗?相信大家仔细思考一番都能理解。
//视频讲解
从对头/队尾取
思路比较简单,除了判断指针pq不为空外还需要判断你取的对头/尾不能为空,原因:你不可能取个寂寞出来对吧,最后注意我们取的不是指针!!!是对头/尾的数值!!!当然啦这里还有一点需要注意的是这两个函数的类型是第一个结构体QueueNode中val的类型
Size函数
int QueueSize(queue* pq)
{
assert(pq);
return pq->size;
}
步骤:1.判空 2.返回size
判空
bool QueueEmpty(que* pq)
{
assert(pq);
return pq->size == 0;
}
思路:判断size是否为0,为0,则真,反之为假。
以上便是本篇文章的基础知识,接下来是例题讲解
例题讲解
例1
题意:创建两个队列实现栈
温馨提示:本题是结合了本篇文章以及上一篇文章栈的内容,用c语言写会比较复杂,请耐心观看
初始化
在已有队列的代码基础上(也就是上面我给大家讲解的基础代码),我们需要创建一个结构体里面包含两个队列,如下:
typedef struct {
// 创建两个队列
Queue q1;
Queue q2;
} MyStack;
之后便是初始化,写到初始化的时候,可能会有小伙伴感到疑惑,不知在这里如何下笔。那么这里教大家一个小技巧:结构体是需要使用->或 " . "(点)来解引用。
技巧解释:->:通常要使用结构体指针来访问结构体成员,而点则是结构体名通过点访问结构体成员
那么,我们这里就是用结构体指针来访问结构体成员。
当我们使用c语言写这道题的时候,题目的解答区是这样的,
因此,我们需要先把上面写的队列代码cv进这个答题区。
在结构体中创建好队列,由于上面已经创好了,因此这里也就不再重复写了,那么我们回到初始化环节,刚刚说到需要用结构体指针来访问成员,但是在这里我们需要创建一个结构体指针,由于这里的初始化与以往有些不一样,因此我会带大家写完。
这里也顺便说一下,也许会有小伙伴说也可以直接创建临时变量来解决,但是临时变量它只能在该函数里使用,无法跳越到其他函数中使用,也许还有小伙伴会说直接创建全局变量,这个想法也是不太行的,有可能会导致程序出现错误。
创建结构体指针:用结构体名* 指针名 = (类型强转)malloc(sizeof(类型));
之后就是对q1和q2进行初始化,初始化结束后,有的小伙伴们就直接开始写下一个函数的代码语句了。
那么我们来思考一下这代码有没有问题,在之前的一篇文章中我有介绍过非void类型的函数都是有返回值的,即便是这个以结构体指针为类型的也是一样的。
那么,这里到底是返回上面呢,我们仔细想一下这个函数是干啥来着?是因为要初始化结构体,然后创键了结构体指针对吧,那么创建并初始化好的结构体指针就是我们所要返回的东西呀,所以这里我们直接返回所创建好的结构体指针。
那么讲完这后可能有小伙伴就直接写成了如下情况,
我们来重新审题,题目要求用队列实现栈,因此我们不能自己去给它初始化而是要放入自己一开始已经写好的队列程序里初始化,当然啦也不能忘记我们传的是地址哦。正确写法如下
入栈
思路:此时,我们两个队列都为空,因此我们随便选择一个队列,若队为空则差队1反正则插队2.
写法如下:
移除并返回栈顶元素
栈的性质:删除栈顶的数据
目标:我们要将非空队的数据导入空的队里。
思路:那么问题来了q1和q2谁空呢?这里我们没法判断,因此我们用假设法,假设q1为空,q2不为空,则将非空的那个队的前n-1个数据导入进空的队,那么剩下的就是栈顶数据了,也就是我们要出栈的数据了,这里我们需要使用循环,并且条件是当size>1时就将数据导进空队中,当只剩下
一个数据时存入临时变量top中并返回top,代码如下:
返回栈顶元素
思路这里我们使用非空队的队尾取元素并返回即可
判空
销毁
基本思路和我之前讲的一样,但这里不能直接free(obj),因为在obj这个指针里还有两个队列因此我们要先销毁队列,再释放obj。
例题2
题目要求:用两个栈实现队列
前置条件:在力扣上用c语言写这题的时候没有栈因此需要用我们自己之前写的栈
结构体指针创建
typedef struct {
Stack pushst;
Stack popst;
} MyQueue;
初始化
由题意可知,这题与上面一样,我们需要先创建一个结构体,并且包含两个栈的指针。
那么,我们在力扣里面写代码时,答题区是这样的。
因此,和上一题一样,我们也需要创建一个指针方便我们去调用这两个栈,创建好后我们需要将其初始化并且返回这个结构体指针 。
将元素推到队列的末尾
使用我们先前写好的入栈函数将x推到队列的末尾,正确代码如下:
思路
20240530_172421
正确写法:
从队列的开头移除并返回元素
思路:我们先将上一个函数返回的StackTop的值存放于新创建的front变量中,再删除掉所取出来的数据,最后返回front即可
判空部分
这里,我们只需要判断两个栈是否都为空,若一个为空或者两个都是空,则返回true,反之,则返回false
销毁部分
与例题一的销毁部分一样的思路
正确写法已经给大家放在上面了,大家可以参考一下。
例题3
题目要求:设计循环队列的实现
这里呢有两种思路,分别是链表和数组,由于博主即将期末考试,因此本篇文章就先给大家讲解数组的思路,至于链表的思路,大家也不必担心,等博主期末考完就给大家补上哈。
思路(数组法):
20240606_184844
因此,tail应该指向队尾的下一个指针
那么源代码如下:
typedef struct {
int* a;
int head;
int tail;
int k;//个数
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a = (int*)malloc(sizeof(int)*(k+1));
obj->head = 0;
obj->tail = 0;
obj->k = k;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->head == obj->tail;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->tail+1) % (obj->k+1) == obj->head;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
if(myCircularQueueIsFull(obj))
{
return false;
}
obj->a[obj->tail] = value;
obj->tail++;
obj->tail %= (obj->k+1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
++obj->head;
obj->head %= (obj->k+1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
else
return obj->a[obj->head];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
else
return obj->a[(obj->tail + obj->k) %(obj->k + 1)];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
那么本期视频就先到这里啦,如有疑惑的地方可以在评论区留言,最后也祝各位大学生期末不挂科,我们下期见,拜拜