片头
嗨! 小伙伴们! 好久不见啦,今天我们来看看这道OJ题---用栈实现队列,准备好了吗? 我们开始咯!
emmm,在上一篇中,我们讲解了怎么用2个队列实现栈,那这一次呢? 那我们就用2个栈实现一个队列吧 !
思路一: 定义2个栈,分别命名为Pushst和Popst,命名为Pushst的栈专门用来存储元素, 命名为Popst的栈专门用来移除栈顶元素。
这里,我先把有关栈的代码放出来哈~
栈的头文件 Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int ElemType;
typedef struct Stack {
ElemType* arr;
int top;
int capacity;
}Stack;
//初始化栈
void StackInit(Stack* st);
//入栈
void StackPush(Stack* st, ElemType data);
//出栈
void StackPop(Stack* st);
//获取栈顶元素
ElemType StackTop(Stack* st);
//获取栈中有效元素的个数
int StackSize(Stack* st);
//检测栈是否为空
int StackEmpty(Stack* st);
//销毁栈
void StackDestroy(Stack* st);
栈的具体操作 Stack.c
#include"Stack.h"
//初始化栈
void StackInit(Stack* st) {
assert(st);
st->arr = NULL;
st->top = 0;
st->capacity = 0;
}
//入栈
void StackPush(Stack* st, ElemType data) {
assert(st);
//扩容
if (st->capacity == st->top) {
int newCapacity = st->capacity == 0 ? 4 : 2 * (st->capacity);
ElemType* temp = realloc(st->arr, newCapacity * sizeof(ElemType));
if (temp == NULL) {
perror("realloc fail!\n");
exit(1);
}
st->arr = temp;
st->capacity = newCapacity;
}
st->arr[st->top] = data;
st->top++;
}
//出栈
void StackPop(Stack* st) {
assert(st);
assert(st->top != 0);
st->top--;
}
//获取栈顶元素
ElemType StackTop(Stack* st) {
assert(st);
assert(st->top != 0);
return st->arr[st->top - 1];
}
//获取栈中有效元素的个数
int StackSize(Stack* st) {
assert(st);
return st->arr[st->top];
}
//检测栈是否为空
int StackEmpty(Stack* st) {
assert(st);
return st->top == 0;
}
//销毁栈
void StackDestroy(Stack* st) {
assert(st);
if (st->arr) {
free(st->arr);
st->arr = NULL;
}
st->capacity = 0;
st->top = 0;
}
聪明的你肯定知道,栈的特点是后进先出(LIFO),队列的特点是先进先出(FIFO),那我们怎么实现用2个栈实现一个队列呢?咱们先来画个图~
①定义2个栈,分别为Pushst(专门用来存储元素)和Popst(专门用来移除栈顶元素),初始化2个栈,2个栈都为空。
②往命名为Pushst的栈里面存储元素, 假设我们存储 1, 2, 3, 4,就可以直接把数据存储到Pushst栈中
③如果此时我们想要获取栈底元素“1”(也就是队头元素“1”),我们本应该从Popst栈里面获取栈顶元素(队头元素),但是现在Popst栈为空。因此,我们需要将Pushst栈里面的元素全部拷贝到Popst栈中。注意啦!Pushst 栈里面的数据拷贝到Popst栈中时,会改变原来的顺序!
举个例子呗~ 还是上面这幅图
第一次: 将Pushst栈的栈顶元素"4"拷贝到Popst栈中,再把"4"这个值从Pushst栈里面删除
第二次: 将Pushst栈的栈顶元素"3"拷贝到Popst栈中,再把"3"这个值从Pushst栈里面删除
第三次: 将Pushst栈的栈顶元素"2"拷贝到Popst栈中,再把"2"这个值从Pushst栈里面删除
第四次:将Pushst栈的栈顶元素"1"拷贝到Popst栈中,再把"1"这个值从Pushst栈里面删除
OK,所有数据从Pushst栈里面拷贝到Popst栈里面,我们可以发现,在Pushst栈里面,数据是以 4->3->2->1 的形式存储的,当数据拷贝到Popst栈里面时, 数据是以 1->2->3->4的形式存储的,刚好符合队列的先进先出的顺序 ,现在Popst的栈顶元素为"1",刚好是我们要查找的队头元素,最后我们将栈顶元素"1"(也就是队头元素"1")返回就可以啦!
④我们要移除队头元素(Popst栈里面的栈顶元素)
哈哈哈,这个问题就是在查询队头元素(查询栈顶元素)的基础上,将栈顶元素删除就可以啦!
方法和上述步骤相同,当Popst栈为空时,将Pushst栈里面所有的数据依次拷贝到Popst栈中,然后将Popst栈的栈顶元素返回,最后将这个Popst栈的栈顶元素移除。
⑤判断2个栈组成的队列是否为空
在上一篇中,我们知道,只有2个队列同时为空时,组成的栈才为空; 那在这一篇中,我们也可以想到,只有当2个栈同时为空时,组成的队列才为空。
⑥销毁队列
实现完由2个栈组成的队列基本操作后,我们就可以销毁这个队列啦! 怎么销毁呢? 那就是先销毁2个栈,最后将申请在堆上的malloc的内存空间释放掉。
OK! 这道题我们讲完了,完整代码如下:
typedef int ElemType;
typedef struct Stack {
ElemType* arr;
int top;
int capacity;
}Stack;
//初始化栈
void StackInit(Stack* st);
//入栈
void StackPush(Stack* st, ElemType data);
//出栈
void StackPop(Stack* st);
//获取栈顶元素
ElemType StackTop(Stack* st);
//获取栈中有效元素的个数
int StackSize(Stack* st);
//检测栈是否为空
int StackEmpty(Stack* st);
//销毁栈
void StackDestroy(Stack* st);
//初始化栈
void StackInit(Stack* st) {
assert(st);
st->arr = NULL;
st->top = 0;
st->capacity = 0;
}
//入栈
void StackPush(Stack* st, ElemType data) {
assert(st);
//扩容
if (st->capacity == st->top) {
int newCapacity = st->capacity == 0 ? 4 : 2 * (st->capacity);
ElemType* temp = realloc(st->arr, newCapacity * sizeof(ElemType));
if (temp == NULL) {
perror("realloc fail!\n");
exit(1);
}
st->arr = temp;
st->capacity = newCapacity;
}
st->arr[st->top] = data;
st->top++;
}
//出栈
void StackPop(Stack* st) {
assert(st);
assert(st->top != 0);
st->top--;
}
//获取栈顶元素
ElemType StackTop(Stack* st) {
assert(st);
assert(st->top != 0);
return st->arr[st->top - 1];
}
//获取栈中有效元素的个数
int StackSize(Stack* st) {
assert(st);
return st->arr[st->top];
}
//检测栈是否为空
int StackEmpty(Stack* st) {
assert(st);
return st->top == 0;
}
//销毁栈
void StackDestroy(Stack* st) {
assert(st);
if (st->arr) {
free(st->arr);
st->arr = NULL;
}
st->capacity = 0;
st->top = 0;
}
typedef struct {
//用一个栈用来填充数据,一个用来删除数据
Stack Pushst;
Stack Popst;
} MyQueue;
MyQueue* myQueueCreate() {
//用malloc申请一块空间给 MyQueue,开辟2个栈的结构体
MyQueue* Q = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&Q->Pushst); //分别将两个栈初始化
StackInit(&Q->Popst);
return Q; //将指向结构体的指针Q返回
}
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->Pushst,x); //往Pushst栈里面填充数据
}
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->Popst)){
//如果Popst栈为空
//那么就将Pushst栈里面所有的元素拷贝到Popst栈中
while(!StackEmpty(&obj->Pushst)){
int top = StackTop(&obj->Pushst);//获取Pushst栈里面的栈顶元素
StackPush(&obj->Popst,top); //拷贝到Popst栈中
StackPop(&obj->Pushst);
}
}
int ret = StackTop(&obj->Popst); //获取Popst栈中的栈顶元素
StackPop(&obj->Popst); //将栈顶元素移除
return ret; //将栈顶元素返回
}
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->Popst)){
//如果Popst栈为空
//那么就将Pushst栈里面所有的元素拷贝到Popst栈中
while(!StackEmpty(&obj->Pushst)){
int top = StackTop(&obj->Pushst);//获取Pushst栈里面的栈顶元素
StackPush(&obj->Popst,top); //拷贝到Popst栈中
StackPop(&obj->Pushst); //将这个元素从Pushst栈中移除
}
}
return StackTop(&obj->Popst); //将栈顶元素返回
}
bool myQueueEmpty(MyQueue* obj) {
//只有当2个栈同时为空,队列才为空
return StackEmpty(&obj->Pushst) && StackEmpty(&obj->Popst);
}
void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->Pushst); //销毁Pushst栈
StackDestroy(&obj->Popst); //销毁Popst栈
free(obj); //将开辟的内存空间返回
}
不过,这里有一个小小的注意点,不知道小伙伴们有木有发现,这两段代码实质上是重复的~
哦! 我知道啦! 我们可以调用myQueuePeek函数来帮助我们实现myQueuePop函数! myQueuePop函数只是在myQueuePeek函数的基础上删除了栈顶元素而已~
好滴! 果然被眼光灵敏的你捕捉到了,接下来我们优化代码~
int myQueuePop(MyQueue* obj) {
int front = myQueuePeek(obj); //获取Popst栈里面的栈顶元素(队头元素)
StackPop(&obj->Popst); //将Popst栈的栈顶元素删除
return front; //返回栈顶元素
}
片尾
今天我们学习了一道OJ题---用栈实现队列,里面涉及到了有关栈和队列的基础知识,希望看完这篇文章能对友友们有所帮助 ! ! !
求点赞收藏加关注 ! ! !
谢谢大家 ! ! !