栈和队列OJ题:LeetCode--232.用栈实现队列

简介: LeetCode--232.用栈实现队列:详细题解以及图解和完整代码。

朋友们、伙计们,我们又见面了,今天给大家带来的是LeetCode--232.用栈实现队列

数 据 结 构 专 栏:数据结构

个    人   主    页 :stackY、

LeetCode 专  栏 :LeetCode刷题训练营

LeetCode--232.用栈实现队列:https://leetcode.cn/problems/implement-queue-using-stacks/

目录

1.题目介绍

2.实例演示

3.解题思路

3.1创建队列

3.2入列

3.3出列

3.4获取队头元素

3.5优化代码

3.6检测队列是否为空

3.7销毁队列

4.完整代码


1.题目介绍

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

       1. void push(int x) 将元素 x 推到队列的末尾

       2. int pop() 从队列的开头移除并返回元素

       3. int peek() 返回队列开头的元素

       4. boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

       你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。

你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

image.gif编辑

2.实例演示

image.gif编辑

3.解题思路

在做题之前我们先来进行简单的梳理逻辑,要通过两个栈来实现队列的功能,首先得清楚栈和队列都有什么区别,不熟悉的宝子可以去看看数据结构:栈和队列,队列的功能先进先出,而栈的功能是后进先出,那么有两个栈,要实现队列这种先进先出的功能就比较简单了,可以进行数据的转化,要将数据出队列,那么就需要将这个栈中的数据的前n-1个数据先插入到另外的一个栈,然后再将最后的元素出栈即可,这样就实现了队列的出列操作,那么在入列的时候我们是往哪个栈中入数据呢?所以我们的两个栈分别要有他们的功能,一个用来出数据,另外一个用来入数据,这样子就比较方便了。

3.1创建队列

使用两个栈来实现队列的功能,首先我们得写出一个栈,队列的功能是先进先出,栈的功能是后进先出,在创建队列的时候需要有两个栈,然后为队列创建一块空间,并且将两个栈都进行初始化:

实现栈:

//动态栈
typedef int STDataType;
typedef struct Stack
{
  STDataType* a;
  int top;        //栈顶
  int capacity;  //栈的容量
}Stack;
//对栈的初始化
void StackInit(Stack* pst);
//入栈
void StackPush(Stack* pst, STDataType x);
//出栈
void StackPop(Stack* pst);
//获取栈顶元素
STDataType StackTop(Stack* pst);
//获取栈中的有效元素的个数
int StackSize(Stack* pst);
//检测栈是否为空
bool StackEmpty(Stack* pst);
//销毁栈
void StackDestroy(Stack* pst);
//对栈的初始化
void StackInit(Stack* pst)
{
  assert(pst);
  pst->a = NULL;
  //top为-1时,插入一个数据之后top指向的是刚刚插入数据的位置
  //pst->top = -1     
  //top为0时,插入一个数据之后top指向的是刚刚插入数据后面的位置
  pst->top = 0;
  pst->capacity = 0;
}
//入栈
void StackPush(Stack* pst, STDataType x)
{
  assert(pst);
  //检测容量
  if (pst->top == pst->capacity)
  {
    int NewCapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
    //当pst->a为NULL时执行的功能是和malloc一样
    STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * NewCapacity);
    if (tmp == NULL)
    {
      perror("realloc fail");
      return;
    }
    pst->a = tmp;
    pst->capacity = NewCapacity;
  }
  //入栈
  pst->a[pst->top] = x;
  pst->top++;
}
//出栈
void StackPop(Stack* pst)
{
  assert(pst);
  //判断栈是否为空
  assert(!StackEmpty(pst));
  //出栈
  pst->top--;
}
//获取栈顶元素
STDataType StackTop(Stack* pst)
{
  assert(pst);
  assert(!StackEmpty(pst));
  //top指向的是栈顶的下一个位置的元素
  return pst->a[pst->top-1];
}
//获取栈中的有效元素的个数
int StackSize(Stack* pst)
{
  assert(pst);
  return pst->top;
}
//检测栈是否为空
bool StackEmpty(Stack* pst)
{
  assert(pst);
  /*if (pst->top == 0)
  {
    return true;
  }
  else
  {
    return false;
  }*/
  return pst->top == 0;
}
//销毁栈
void StackDestroy(Stack* pst)
{
  assert(pst);
  //释放
  free(pst->a);
  pst->a = NULL;
  //重置为0
  pst->top = pst->capacity = 0;
}
image.gif

创建队列:

typedef struct {
    Stack STPush;  //用来出数据的栈
    Stack STPop;   //用来入数据的栈
} MyQueue;
MyQueue* myQueueCreate() {
    //先为队列开辟一块空间
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    if(obj == NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    //再初始化栈
    StackInit(&obj->STPush);
    StackInit(&obj->STPop);
    return obj;
}
image.gif

3.2入列

插入数据就非常简单了,直接将数据插入到我们指定的STPush这个栈中:

代码演示:

void myQueuePush(MyQueue* obj, int x) {
    StackPush(&obj->STPush, x);
}
image.gif

3.3出列

栈的出栈是先进后出,我们有两个栈,因此可以将STPush栈中的前n-1个数据先依次插入到STPop栈中,然后将剩下的最后一个元素,对于队列来说就是队头的数据进行删除即可,但是呢,当第二次删除的时候STPush中没有数据了,这时的STPop中的数据的排列刚好是队列的这种效果,所以可以直接在STPop栈中直接删除即可,所以出列操作需要分两种情况:STPop栈中有无数据

image.gif编辑

代码演示:

int myQueuePop(MyQueue* obj) {
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //导数据
        while (StackSize(&obj->STPush) > 1)
    {
      StackPush(&obj->STPop, StackTop(&obj->STPush));
      StackPop(&obj->STPush);
    }
        //保存队头元素
        int top = StackTop(&obj->STPush);
        //删除
        StackPop(&obj->STPush);
        return top;
    }
    else
    {
        int top = StackTop(&obj->STPop);
    StackPop(&obj->STPop);
    return top;
    }
}
image.gif

3.4获取队头元素

获取队头元素这里就比较有点麻烦了,先要知道队头元素在哪个栈中,由于我们刚开始设定的缘故,队头的数据是在STPop这个栈中的,这时就需要分两种情况,如果STPop为空,这时就要将STPush中的数据全部插入到STPop中,插入过来之后STPop中的栈顶的数据就是对头的数据,如果STPop不为空,就不需要进行导数据,STPop中的栈顶元素也就是对头的数据:

image.gif编辑

代码演示:

int myQueuePeek(MyQueue* obj) {
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //全部导过去
        while (!StackEmpty(&obj->STPush))
    {
      StackPush(&obj->STPop, StackTop(&obj->STPush));
      StackPop(&obj->STPush);
    }
    }
    //再取栈顶元素
    return StackTop(&obj->STPop);
}
image.gif

3.5优化代码

如果将出列和获取队头元素这两个函数合在一起看的话还是有许多相似之处,因此我们可以进行改造,在出列时可以直接复用获取队头元素这个函数,这时表现出来的结果就是将STPush中的所有元素都挪动到STPop中,然后再进行删除即可:

image.gif编辑

代码演示:

int myQueuePop(MyQueue* obj) {
    /*
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //导数据
        while (StackSize(&obj->STPush) > 1)
    {
      StackPush(&obj->STPop, StackTop(&obj->STPush));
      StackPop(&obj->STPush);
    }
        //保存队头元素
        int top = StackTop(&obj->STPush);
        //删除
        StackPop(&obj->STPush);
        return top;
    }
    else
    {
        int top = StackTop(&obj->STPop);
    StackPop(&obj->STPop);
    return top;
    }
    */
    //复用
    int front = myQueuePeek(obj);
    //删除
    StackPop(&obj->STPop);
    return front;
}
int myQueuePeek(MyQueue* obj) {
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //全部导过去
        while (!StackEmpty(&obj->STPush))
    {
      StackPush(&obj->STPop, StackTop(&obj->STPush));
      StackPop(&obj->STPush);
    }
    }
    //再取栈顶元素
    return StackTop(&obj->STPop);
}
image.gif

3.6检测队列是否为空

队列若为空就表示两个栈都为空,若一个栈不为空,则队列就不为空:

代码演示:

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->STPush)
    && StackEmpty(&obj->STPop);
}
image.gif

3.7销毁队列

要销毁队列先要销毁两个栈,然后再将队列释放:

代码演示:

void myQueueFree(MyQueue* obj) {
    //先释放栈
    StackDestroy(&obj->STPush);
    StackDestroy(&obj->STPop);
    //再释放队列
    free(obj);
}
image.gif

4.完整代码

//动态栈
typedef int STDataType;
typedef struct Stack
{
  STDataType* a;
  int top;        //栈顶
  int capacity;  //栈的容量
}Stack;
//对栈的初始化
void StackInit(Stack* pst);
//入栈
void StackPush(Stack* pst, STDataType x);
//出栈
void StackPop(Stack* pst);
//获取栈顶元素
STDataType StackTop(Stack* pst);
//获取栈中的有效元素的个数
int StackSize(Stack* pst);
//检测栈是否为空
bool StackEmpty(Stack* pst);
//销毁栈
void StackDestroy(Stack* pst);
//对栈的初始化
void StackInit(Stack* pst)
{
  assert(pst);
  pst->a = NULL;
  //top为-1时,插入一个数据之后top指向的是刚刚插入数据的位置
  //pst->top = -1     
  //top为0时,插入一个数据之后top指向的是刚刚插入数据后面的位置
  pst->top = 0;
  pst->capacity = 0;
}
//入栈
void StackPush(Stack* pst, STDataType x)
{
  assert(pst);
  //检测容量
  if (pst->top == pst->capacity)
  {
    int NewCapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
    //当pst->a为NULL时执行的功能是和malloc一样
    STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * NewCapacity);
    if (tmp == NULL)
    {
      perror("realloc fail");
      return;
    }
    pst->a = tmp;
    pst->capacity = NewCapacity;
  }
  //入栈
  pst->a[pst->top] = x;
  pst->top++;
}
//出栈
void StackPop(Stack* pst)
{
  assert(pst);
  //判断栈是否为空
  assert(!StackEmpty(pst));
  //出栈
  pst->top--;
}
//获取栈顶元素
STDataType StackTop(Stack* pst)
{
  assert(pst);
  assert(!StackEmpty(pst));
  //top指向的是栈顶的下一个位置的元素
  return pst->a[pst->top-1];
}
//获取栈中的有效元素的个数
int StackSize(Stack* pst)
{
  assert(pst);
  return pst->top;
}
//检测栈是否为空
bool StackEmpty(Stack* pst)
{
  assert(pst);
  /*if (pst->top == 0)
  {
    return true;
  }
  else
  {
    return false;
  }*/
  return pst->top == 0;
}
//销毁栈
void StackDestroy(Stack* pst)
{
  assert(pst);
  //释放
  free(pst->a);
  pst->a = NULL;
  //重置为0
  pst->top = pst->capacity = 0;
}
typedef struct {
    Stack STPush;  //用来出数据的栈
    Stack STPop;   //用来入数据的栈
} MyQueue;
MyQueue* myQueueCreate() {
    //先为队列开辟一块空间
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    if(obj == NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    //再初始化栈
    StackInit(&obj->STPush);
    StackInit(&obj->STPop);
    return obj;
}
void myQueuePush(MyQueue* obj, int x) {
    StackPush(&obj->STPush, x);
}
int myQueuePop(MyQueue* obj) {
    /*
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //导数据
        while (StackSize(&obj->STPush) > 1)
    {
      StackPush(&obj->STPop, StackTop(&obj->STPush));
      StackPop(&obj->STPush);
    }
        //保存队头元素
        int top = StackTop(&obj->STPush);
        //删除
        StackPop(&obj->STPush);
        return top;
    }
    else
    {
        int top = StackTop(&obj->STPop);
    StackPop(&obj->STPop);
    return top;
    }
    */
    //复用
    int front = myQueuePeek(obj);
    //删除
    StackPop(&obj->STPop);
    return front;
}
int myQueuePeek(MyQueue* obj) {
    //判断STPop是否为空
    if(StackEmpty(&obj->STPop))
    {
        //全部导过去
        while (!StackEmpty(&obj->STPush))
    {
      StackPush(&obj->STPop, StackTop(&obj->STPush));
      StackPop(&obj->STPush);
    }
    }
    //再取栈顶元素
    return StackTop(&obj->STPop);
}
bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->STPush)
    && StackEmpty(&obj->STPop);
}
void myQueueFree(MyQueue* obj) {
    //先释放栈
    StackDestroy(&obj->STPush);
    StackDestroy(&obj->STPop);
    //再释放队列
    free(obj);
}
/**
 * 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);
*/
image.gif

今天的博客就分享到这里,喜欢的老铁留下你的三连,感谢感谢!我们下期再见!!

目录
相关文章
|
4月前
|
存储 算法 测试技术
力扣经典150题第五十四题:最小栈
力扣经典150题第五十四题:最小栈
38 0
|
5月前
|
存储 算法 索引
力扣每日一题 6/24 模拟 数组 单调栈
力扣每日一题 6/24 模拟 数组 单调栈
35 0
|
1月前
【LeetCode 24】225.用队列实现栈
【LeetCode 24】225.用队列实现栈
12 0
|
1月前
|
算法
【LeetCode 23】232.用栈实现队列
【LeetCode 23】232.用栈实现队列
19 0
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
28 4
|
3月前
|
Python
【Leetcode刷题Python】946. 验证栈序列
LeetCode题目“946. 验证栈序列”的Python解决方案,通过模拟栈的压入和弹出操作来验证给定的两个序列是否能通过合法的栈操作得到。
30 6
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 09. 用两个栈实现队列
使用两个栈实现队列的Python解决方案,包括初始化两个栈、实现在队列尾部添加整数的appendTail方法和在队列头部删除整数的deleteHead方法,以及相应的示例操作。
39 2
|
3月前
|
Python
【Leetcode刷题Python】641.循环双端队列
文章介绍了如何实现一个循环双端队列,包括其操作如插入、删除、获取队首和队尾元素,以及检查队列是否为空或已满,并提供了Python语言的实现代码。
25 0
|
3月前
|
Python
【Leetcode刷题Python】232. 用栈实现队列
如何使用Python语言通过两个栈来实现队列的所有基本操作,包括入队(push)、出队(pop)、查看队首元素(peek)和判断队列是否为空(empty),并提供了相应的代码实现。
21 0
|
5月前
|
存储 算法 Python
二刷力扣--栈和队列
二刷力扣--栈和队列