【LeetCode数据结构】栈和队列的应用——用队列实现栈问题、用栈实现队列问题详解
🔥个人主页:艾莉丝努力练剑
❄专栏传送门:《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题
🍉学习方向:C/C++方向
⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平
前言:牛客网和LeetCode的刷题都不可或缺,我们都要做一做,无论是参加竞赛还是笔试面试,至少能提升你的代码能力!洛谷的题目也可以去做一做。力扣的题目对提升代码能力很有帮助,需要有一点基础,几乎都是接口型的题目,关于接口型和IO型的区别我们在本专栏的第一篇【LeetCode】力扣题——轮转数组、消失的数字、数组串联中就介绍过了,这里不再赘述,我们进入今天的力扣题目介绍——
目录
正文
一、用队列实现栈问题
(一)思路
(二)解题过程
1、初始化
2、入栈
3、出栈
4、取栈顶元素
5、判断栈是否为空
6、销毁栈
(三)完整代码
二、用栈实现队列问题
(一)思路
(二)解题过程
1、初始化
2、入队列
3、出队列
4、取队头
5、判断为空
6、销毁
(三)完整代码
结尾
正文
一、用队列实现栈问题
225.用队列实现栈
博主题解链接:调用队列结构实现栈
题目描述:
题目示例和提示——
(一)思路
我们的思路是:入栈:往不为空的队列中插入数据;出栈:把不为空队列中前size-1个数据挪到另一个队列,再将最后一个数据出队列;取栈顶(不出数据):找不为空的队尾数据,返回队尾数据(我们之前实现过取队尾操作)。如下图——
(二)解题过程
既然是用队列实现栈问题,那我们直接调用队列。
1、初始化
前面已经调用了队列的头文件和各种方法,我们就不用自己写了。
//创建一个栈
MyStack* myStackCreate()
{MyStack* pst = (MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);//把q1的地址传过去QueueInit(&pst->q2);return pst;
}
2、入栈
入栈的代码——
//往栈里面插入一个栈顶元素
void myStackPush(MyStack* obj, int x)
{//往不为空的队列里面插入数据if!(QueueEmpty(&obj->q1))//传q1的地址{//q1QueuePush(&obj->q1,x);}else{//q2QueuePush(&obj->q2,x);}
}
3、出栈
//不为空队列前size-1个数据挪到空队列中while(QueueSize(&noneEmp) > 1){//取队头int front = QueueFront(&noneEmp);//入另一个队列QueuePush(&emp,front);//出队头QueuePop(&noneEmp)}
这里可以合并一下,反正返回值要作为第二个方法的front,那这里我们其实可以不创建临时变量,我们直接让返回值作为第二个参数——先取队头,再入到另一个队列当中——
//取队头,入另一个队列
QueuePush(&emp,QueueFront(&noneEmp));
//出队头
QueuePop(&noneEmp)
出栈的代码——
//往栈里面删除一个栈顶元素
int myStackPop(MyStack* obj)
{//找不为空队列//类似于前面假设长短链表的方法,这里假设空和非空Queue* emp = &obj->q1;Queue* noneEmp = &obj->q2;if(QueueEmpty(&obj->q2))//这里是不加!的{emp = &obj->q2;noneEmp = &obj->q1;}//不为空队列前size-1个数据挪到空队列中while(QueueSize(&noneEmp) > 1){//取队头,入另一个队列QueuePush(&emp,QueueFront(&noneEmp));//出队头QueuePop(&noneEmp)}//不为空队列数据出队int top = QueueFront(&noneEmp);QueuePop(&noneEmp);return top;
}
返回top值——
4、取栈顶元素
//取栈顶元素
int myStackTop(MyStack* obj)
{//找不为空队列,返回不为空队列的队尾数据if(!QueueEmpty(&obj->q1)){return QueueBack(&obj->q1);}else{return QueueBack(&obj->q2);}
}
5、判断栈是否为空
//判断栈是否为空
bool myStackEmpty(MyStack* obj)
{return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
6、销毁栈
//销毁栈
void myStackFree(MyStack* obj)
{QueueDestory(&obj->q1);QueueDestory(&obj->q2);free(obj);obj = NULL;
}
(三)完整代码
typedef int QDataType;
//定义节点的结构typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QueueNode;//定义队列的结构
typedef struct Queue
{QueueNode* phead;//指向队头节点的指针QueueNode* ptail;//指向队尾节点的指针int size; //队列中有效数据个数
}Queue;//初始化
void QueueInit(Queue* pq);//销毁
void QueueDestory(Queue* pq);//入队列,队尾
void QueuePush(Queue* pq, QDataType x);// 出队列,队头
void QueuePop(Queue* pq);//队列判空
bool QueueEmpty(Queue* pq);//取队头数据
QDataType QueueFront(Queue* pq);//取队尾数据
QDataType QueueBack(Queue* pq);//队列有效元素个数
int QueueSize(Queue* pq);//初始化
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}//销毁队列
void QueueDestory(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while(pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail = NULL;//防止phead、ptail变野指针pq->size = 0;//销毁后元素个数重置为0
}//入队列,队尾
void QueuePush(Queue* pq, QDataType x)
{int size = 0;assert(pq);//创建值为x的节点QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;//队列为空if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = pq->ptail->next;}pq->size++;//入队后元素个数+1
}//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL;
}// 出队列,队头
void QueuePop(Queue* pq)
{assert(!QueueEmpty(pq));//队列中只有一个节点if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else {QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}pq->size--;//出队后元素个数-1
}//取队头数据
QDataType QueueFront(Queue* pq)
{ assert(!QueueEmpty(pq));return pq->phead->data;
}//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->ptail->data;
}//队列有效元素个数
int QueueSize(Queue* pq)
{/*assert(pq);QueueNode* pcur = pq->phead;int size = 0;while (pcur){++size;pcur = pcur->next;}return size;*/return pq->size;
}
//--------------------以上是队列的结构和常用方法----------------------//两个队列实现一个栈
typedef struct
{Queue q1;Queue q2;
} MyStack;//创建一个栈
MyStack* myStackCreate()
{MyStack* pst = (MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);//把q1的地址传过去QueueInit(&pst->q2);return pst;
}
//往栈里面插入一个栈顶元素
void myStackPush(MyStack* obj, int x) {//往不为空的队列里面插入数据if(!QueueEmpty(&obj->q1))//传q1的地址{//q1QueuePush(&obj->q1, x);}else {//q2QueuePush(&obj->q2, x);}
}
//往栈里面删除一个栈顶元素
int myStackPop(MyStack* obj) {//找不为空队列//类似于前面假设长短链表的方法,这里假设空和非空Queue* emp = &obj->q1;Queue* noneEmp = &obj->q2;if (QueueEmpty(&obj->q2))//这里是不加!的{emp = &obj->q2;noneEmp = &obj->q1;}//不为空队列前size-1个数据挪到空队列中while (QueueSize(noneEmp) > 1)//这里不用取地址,本身就已经是地址了{//取队头,入另一个队列QueuePush(emp, QueueFront(noneEmp));//出队头QueuePop(noneEmp);}//不为空队列数据出队int top = QueueFront(noneEmp);QueuePop(noneEmp);return top;
}
//取栈顶元素
int myStackTop(MyStack* obj) {//找不为空队列,返回不为空队列的队尾数据if(!QueueEmpty(&obj->q1)){return QueueBack(&obj->q1);}else{return QueueBack(&obj->q2);}
}
//判断栈是否为空
bool myStackEmpty(MyStack* obj) {return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
//销毁栈
void myStackFree(MyStack* obj) {QueueDestory(&obj->q1);QueueDestory(&obj->q2);free(obj);obj = NULL;
}/*** Your MyStack struct will be instantiated and called as such:* MyStack* obj = myStackCreate();* myStackPush(obj, x);* int param_2 = myStackPop(obj);* int param_3 = myStackTop(obj);* bool param_4 = myStackEmpty(obj);* myStackFree(obj);
*/
二、用栈实现队列问题
232.用栈实现队列
博主题解链接:三步走方法用栈实现队列
题目描述:
题目示例和提示——
(一)思路
题目分析:
我们先画一下看看——
思路1:
思路2:
因此,我们的思路还是三步走:
(二)解题过程
1、初始化
//初始化
MyQueue* myQueueCreate()
{MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));//栈的初始化STInit(&pq->pushST);//STInit的形参是一个一级指针,要传地址STInit(&pq->popST);return pq;
}
2、入队列
//入队列
void myQueuePush(MyQueue* obj, int x)
{//往pushST中插入数据STPush(&obj->pushST);
}
3、出队列
刚才我们分析过了,出队列要判断popST为不为空——
//出队列——要判断popST为不为空
int myQueuePop(MyQueue* obj)
{//popST为空——将pushST(不为空)导入到popSTif(STEmpty(&obj->popST){while(!STEmpty(&obj->pushST)){//取栈顶(pushST),入popST栈,出pushST栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接出int top = STTop(&obj->popST);STPop(&obj->popST);return top;
}
4、取队头
取队头其实和出队列几乎一样,只不过取队头不用删除数据——
//取队头
int myQueuePeek(MyQueue* obj)
{//popST为空——将pushST(不为空)导入到popSTif(STEmpty(&obj->popST)){while(!STEmpty(&obj->pushST)){//取栈顶(pushST),入popST栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接取数据int top = STTop(&obj->popST);return top;
}
5、判断为空
两个栈为空,才能为空——
//判断是否为空
bool myQueueEmpty(MyQueue* obj)
{return STEmpty(&obj->popST) && STEmpty(&obj->pushST);
}
6、销毁
对队列进行销毁操作:
1、初始化那儿有个指向队列的指针是malloc出来的,得销毁;
2、在队列里面有两个栈也是向操作空间malloc出来的,也要销毁。
void myQueueFree(MyQueue* obj)
{STDestory(&obj->pushST);STDestory(&obj->popST);free(obj);obj = NULL;
}
(三)完整代码
typedef int STDataType;
typedef struct Stack
{STDataType* arr;int top;//定义栈中有效的数据个数int capacity;//栈的空间大小
}ST;//初始化
void STInit(ST* ps)
{ps->arr = NULL;ps->top = ps->capacity = 0;
}//销毁
void STDestory(ST* ps)
{if (ps->arr)free(ps->arr);ps->arr = NULL;ps->top = ps->capacity = 0;
}//入栈——栈顶
void STPush(ST* ps, STDataType x)
{assert(ps);//判断空间是否足够if (ps->top == ps->capacity){//增容int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}ps->arr = tmp;ps->capacity = newCapacity;}//空间足够ps->arr[ps->top++] = x;
}//栈是否为空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}//出栈——栈顶
void STPop(ST* ps)
{assert(!STEmpty(ps));ps->top--;
}//取栈顶元素
STDataType STTop(ST* ps)
{assert(!STEmpty(ps));return ps->arr[ps->top - 1];
}//获取栈中有效元素个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}
//-----------------以上栈的结构定义和常见的方法----------------------
typedef struct {ST pushST;ST popST;
} MyQueue;//初始化
MyQueue* myQueueCreate() {MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));//栈的初始化STInit(&pq->pushST);//STInit的形参是一个一级指针,要传地址STInit(&pq->popST);return pq;
}
//入队列
void myQueuePush(MyQueue* obj, int x) {//往pushST中插入数据STPush(&obj->pushST,x);
}
//出队列——要判断popST为不为空
int myQueuePop(MyQueue* obj) {//popST为空——将pushST(不为空)导入到popSTif(STEmpty(&obj->popST)){while(!STEmpty(&obj->pushST)){//取栈顶(pushST),入popST栈,出pushST栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接出int top = STTop(&obj->popST);STPop(&obj->popST);return top;
}
//取队头
int myQueuePeek(MyQueue* obj) {//popST为空——将pushST(不为空)导入到popSTif(STEmpty(&obj->popST)){while(!STEmpty(&obj->pushST)){//取栈顶(pushST),入popST栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接取数据int top = STTop(&obj->popST);return top;
}
//判断为空
bool myQueueEmpty(MyQueue* obj) {return STEmpty(&obj->popST) && STEmpty(&obj->pushST);
}
//对队列进行销毁操作
//1.初始化那儿有个指向队列的指针是malloc出来的,得销毁
//2.在队列里面有两个栈也是向操作空间malloc出来的,也要销毁
void myQueueFree(MyQueue* obj) {STDestory(&obj->pushST);STDestory(&obj->popST);free(obj);obj = NULL;
}/*** Your MyQueue struct will be instantiated and called as such:* MyQueue* obj = myQueueCreate();* myQueuePush(obj, x);* int param_2 = myQueuePop(obj);* int param_3 = myQueuePeek(obj);* bool param_4 = myQueueEmpty(obj);* myQueueFree(obj);
*/
结尾
往期回顾:
【LeetCode&数据结构】栈的应用——有效的括号问题详解
由于本专栏的篇数越来越多,为了避免文章链接挂太多影响观感,博主之后的每一篇力扣刷题详解都只会附上前一篇的链接,最后一次完整链接是在之后会发布的【栈的应用——有效的括号问题详解】和【单链表的应用——环形链表问题详解】的文章结尾,本意是为了方便大家找到相应的详细的题解,现在文章多了,铸币博主没办法一一罗列,而且还会有“凑字数”的嫌疑,力扣刷题专栏的链接每次都会放在文章开头的位置,大家可自行前往!
感谢大家的理解与支持!
【LeetCode&数据结构】单链表的应用——环形链表问题详解
结语:本篇文章到这里就结束了,本文讲述的两道代码题并不适合C语言初学者,需要有一定的C语言基础,最好要学过数据结构与算法的算法复杂度和链表的知识,才能写出复杂度较优的代码来。大家一定要自己动手敲一敲,不敲的话不仅容易忘记,也不方便将来复习。