栈和队列oj题——232. 用栈实现队列

发布时间:2024年01月04日

在这里插入图片描述在这里插入图片描述

.

个人主页:晓风飞
专栏:LeetCode刷题|数据结构|Linux|C语言
路漫漫其修远兮,吾将上下而求索



要做题目的点击这里–>栈和队列oj题——232. 用栈实现队列

题目要求:

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:

你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:

输入:
[“MyStack”, “push”, “push”, “top”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:

MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

提示:

1 <= x <= 9
最多调用100 次 push、pop、top 和 empty
每次调用 pop 和 top 都保证栈不为空

进阶:你能否仅用一个队列来实现栈。
在这里插入图片描述

解题核心概念

在我们的实现中,我们定义了两个栈 s1 和 s2。栈 s1 负责处理入队操作,而栈 s2 负责出队操作。通过这种方式,我们可以保证队列的 FIFO 特性,即首先入队的元素将首先出队。

数据结构的定义

首先,我们定义了栈的结构体 ST,它包含一个指向数组的指针、一个表示栈顶的变量和一个表示栈容量的变量。然后,我们定义了队列的结构体 MyQueue,它包含两个栈 s1 和 s2。

// 使用两个栈实现的队列结构体定义
typedef struct 
{
    ST s1; // 第一个栈,用于入队操作
    ST s2; // 第二个栈,用于出队操作
} MyQueue;

初始化队列

使用 myQueueCreate 函数来初始化队列。在这个函数中,我们分配内存给 MyQueue 结构体,并初始化两个栈。

MyQueue* myQueueCreate() 
{
    MyQueue* newQu = (MyQueue*)malloc(sizeof(MyQueue));
    STInit(&newQu->s1); // 初始化第一个栈
    STInit(&newQu->s2); // 初始化第二个栈    
    return newQu; // 返回新创建的队列
}

入队操作

入队操作非常简单:我们只需将元素推入栈 s1。

// 将一个元素推入队列
void myQueuePush(MyQueue* obj, int x) 
{
    STPush(&obj->s1, x); // 将元素推入第一个栈
}

出队操作

出队操作稍微复杂一些。我们需要确保队列的 FIFO 性质,所以当我们想要进行出队操作时,我们首先检查栈 s2。如果栈 s2 为空,我们将栈 s1 中的所有元素逆序转移到栈 s2 中,然后从栈 s2 中弹出元素。

// 如有必要,将元素从第一个栈转移到第二个栈
void TransferIfNeeded(MyQueue* obj) 
{
    if (STEmpty(&obj->s2)) // 检查第二个栈是否为空
    {
        while (!STEmpty(&obj->s1)) // 当第一个栈不为空时
        {
            // 将第一个栈的栈顶元素转移到第二个栈
            STPush(&obj->s2, STTop(&obj->s1));
            STPop(&obj->s1); // 弹出第一个栈的栈顶元素
        }
    }
}

// 从队列中弹出一个元素
int myQueuePop(MyQueue* obj) 
{
    TransferIfNeeded(obj); // 确保所有元素都在第二个栈
    int Top = STTop(&obj->s2); // 获取第二个栈的栈顶元素
    STPop(&obj->s2); // 弹出第二个栈的栈顶元素
    return Top; // 返回弹出的元素
}

查看队列前端元素

查看队列前端元素的实现与出队操作类似,但不移除元素。

// 查看队列的前端元素
int myQueuePeek(MyQueue* obj) 
{
    TransferIfNeeded(obj); // 确保所有元素都在第二个栈
    return STTop(&obj->s2); // 返回第二个栈的栈顶元素
}

检查队列是否为空

队列为空的条件是两个栈都为空。

// 检查队列是否为空
bool myQueueEmpty(MyQueue* obj) 
{
    // 如果两个栈都为空,那么队列为空
    return STEmpty(&obj->s1) && STEmpty(&obj->s2);   
}

释放队列

最后,我们需要一个函数来释放队列占用的资源。

// 释放队列所占用的资源
void myQueueFree(MyQueue* obj) 
{
    STDestroy(&obj->s1); // 销毁第一个栈
    STDestroy(&obj->s2); // 销毁第二个栈
    free(obj); // 释放队列结构体占用的内存   
}

以下是栈的实现:

typedef int STDataType;
typedef struct Stack
{
  STDataType* a;
  STDataType top; 
  STDataType capacity;
}ST;

void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst,STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);

// 初始化栈
void STInit(ST* pst)
{
  assert(pst);
  pst->a = NULL;       // 初始时,数组指针为空
  pst->top = 0;        // 栈顶指针初始为0,表示栈为空
  pst->capacity = 0;   // 初始容量为0
}

// 销毁栈
void STDestroy(ST* pst)
{
  assert(pst);
  free(pst->a);        // 释放栈内部的数组空间
  pst->a = NULL;       // 将数组指针置为空
  pst->top = 0;        // 栈顶指针重置为0
  pst->capacity = 0;   // 容量重置为0
}

// 检查并扩展栈的容量
void SLCheckCapacity(ST* pst)
{
  assert(pst);
  if (pst->top == pst->capacity)
  {
    int newCapacity = (pst->capacity == 0) ? 4 : pst->capacity * 2;
    STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newCapacity);
    if (tmp == NULL)
    {
      perror("realloc fail");
      exit(1); // 如果内存分配失败,则退出程序
    }

    pst->a = tmp;
    pst->capacity = newCapacity;
  }
}

// 向栈中推入一个元素
void STPush(ST* pst, STDataType x)
{
  assert(pst);
  SLCheckCapacity(pst); // 检查并扩展容量
  pst->a[pst->top] = x; // 存放元素
  pst->top++;           // 栈顶指针增加
}

// 从栈中弹出一个元素
void STPop(ST* pst)
{
  assert(pst);
  assert(pst->top > 0); // 确保栈不为空
  pst->top--;           // 栈顶指针减少
}

// 获取栈顶元素
STDataType STTop(ST* pst)
{
  assert(pst);
  assert(pst->top > 0); // 确保栈不为空
  return pst->a[pst->top - 1]; // 返回栈顶元素
}

// 检查栈是否为空
bool STEmpty(ST* pst)
{
  assert(pst);
  return pst->top == 0; // 如果栈顶指针为0,则栈为空
}

// 获取栈的大小
int STSize(ST* pst)
{
  assert(pst);
  return pst->top; // 返回栈顶指针的位置,即栈的大小
}

以下是本题的实现:

   
// 使用两个栈实现的队列结构体定义
typedef struct 
{
    ST s1; // 第一个栈,用于入队操作
    ST s2; // 第二个栈,用于出队操作
} MyQueue;

// 创建并初始化队列
MyQueue* myQueueCreate() 
{
    MyQueue* newQu = (MyQueue*)malloc(sizeof(MyQueue));
    STInit(&newQu->s1); // 初始化第一个栈
    STInit(&newQu->s2); // 初始化第二个栈    
    return newQu; // 返回新创建的队列
}

// 将一个元素推入队列
void myQueuePush(MyQueue* obj, int x) 
{
    STPush(&obj->s1, x); // 将元素推入第一个栈
}

// 如有必要,将元素从第一个栈转移到第二个栈
void TransferIfNeeded(MyQueue* obj) 
{
    if (STEmpty(&obj->s2)) // 检查第二个栈是否为空
    {
        while (!STEmpty(&obj->s1)) // 当第一个栈不为空时
        {
            // 将第一个栈的栈顶元素转移到第二个栈
            STPush(&obj->s2, STTop(&obj->s1));
            STPop(&obj->s1); // 弹出第一个栈的栈顶元素
        }
    }
}

// 从队列中弹出一个元素
int myQueuePop(MyQueue* obj) 
{
    TransferIfNeeded(obj); // 确保所有元素都在第二个栈
    int Top = STTop(&obj->s2); // 获取第二个栈的栈顶元素
    STPop(&obj->s2); // 弹出第二个栈的栈顶元素
    return Top; // 返回弹出的元素
}

// 查看队列的前端元素
int myQueuePeek(MyQueue* obj) 
{
    TransferIfNeeded(obj); // 确保所有元素都在第二个栈
    return STTop(&obj->s2); // 返回第二个栈的栈顶元素
}

// 检查队列是否为空
bool myQueueEmpty(MyQueue* obj) 
{
    // 如果两个栈都为空,那么队列为空
    return STEmpty(&obj->s1) && STEmpty(&obj->s2);   
}

// 释放队列所占用的资源
void myQueueFree(MyQueue* obj) 
{
    STDestroy(&obj->s1); // 销毁第一个栈
    STDestroy(&obj->s2); // 销毁第二个栈
    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);
*/
文章来源:https://blog.csdn.net/2203_75397752/article/details/135373430
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。