LeetCode232. 用栈实现队列

232. 用栈实现队列


一、题目

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

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

示例 1:

输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

提示:

  • 1 <= x <= 9
  • 最多调用 100pushpoppeekempty
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

进阶:

  • 你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。
二、解法
算法思路

该代码实现了一个队列(MyQueue)的数据结构,使用两个栈(stack)来实现队列的操作。栈stIn用于存储插入操作(push)的元素,栈stOut用于存储弹出操作(pop)的元素。当需要执行弹出操作时,如果栈stOut为空,则将栈stIn中的元素逐个弹出并压入栈stOut,然后从栈stOut中弹出元素。这样就实现了先进先出的队列操作。

具体实现
  1. 在构造函数MyQueue()中,不需要执行任何操作,因为栈stIn和栈stOut会在插入和弹出操作中被初始化。
  2. 在插入操作push(int x)中,将元素x压入栈stIn。
  3. 在弹出操作pop()中,首先判断栈stOut是否为空。如果为空,则将栈stIn中的元素逐个弹出并压入栈stOut,然后从栈stOut中弹出元素并返回。
  4. 在查看队头元素操作peek()中,调用已有的pop()函数获取栈stOut的顶部元素,将该元素保存到变量res中。由于pop()函数已经弹出了元素res,所以需要将该元素再次压入栈stOut,然后返回res。
  5. 在判断队列是否为空操作empty()中,如果栈stIn和栈stOut都为空,则返回true;否则返回false。
class MyQueue {
public:
    stack<int> stIn;
    stack<int> stOut;
    MyQueue() {

    }
    
    void push(int x) {
        stIn.push(x);
    }
    
    int pop() {
        if(stOut.empty()){
            while(!stIn.empty()){
                stOut.push(stIn.top());
                stIn.pop();
            }
        }
        int result = stOut.top();
        stOut.pop();
        
        return result;
    }
    
    int peek() {
        //return stOut.top();
        int res = this->pop(); // 直接使用已有的pop函数
        stOut.push(res); // 因为pop函数弹出了元素res,所以再添加回去
        return res;
    }
    
    bool empty() {
        return (stIn.empty() && stOut.empty());
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */
算法分析
  • push操作的时间复杂度为O(1),因为只需要将元素压入栈stIn。
  • pop操作的时间复杂度为O(n),其中n是栈stIn中的元素数量。当栈stOut为空时,需要将栈stIn中的所有元素逐个弹出并压入栈stOut,因此时间复杂度为O(n)。但是,在连续的pop操作中,栈stOut中已经存储了之前的元素,所以每个元素只需要弹出一次,平均下来每个元素的弹出时间复杂度为O(1)。
  • peek操作的时间复杂度为O(1),因为只需要调用已有的pop()函数一次。
  • empty操作的时间复杂度为O(1),因为只需要判断栈stIn和栈stOut是否为空。

总结: 通过使用两个栈实现队列的操作,该代码实现了队列的插入、弹出、查看队头元素和判断队列是否为空的功能。插入操作的时间复杂度为O(1),弹出和查看队头元素的操作的平均时间复杂度为O(1),判断队列是否为空的操作的时间复杂度为O(1)。

三、一些拓展
拓展知识:使用栈实现队列的方法

使用两个栈实现队列的方法是一种常见的解法,称为"双栈法"。这种方法通过利用两个栈的先进后出(LIFO)和后进先出(FIFO)特性,实现了先进先出(FIFO)的队列操作。

使用双栈法实现队列的主要思想是,使用一个栈(stIn)来处理插入操作(push),将元素按照插入顺序压入栈stIn;使用另一个栈(stOut)来处理弹出操作(pop)和查看队头元素操作(peek)。当需要执行弹出或查看队头元素操作时,如果栈stOut为空,则将栈stIn中的元素逐个弹出并压入栈stOut,然后从栈stOut中弹出元素或查看队头元素。这样就实现了先进先出的队列操作。

这种双栈法的优点是插入操作(push)的时间复杂度为O(1),而弹出操作(pop)和查看队头元素操作(peek)的平均时间复杂度也为O(1),因为每个元素只需要弹出一次。另外,这种方法具有较好的空间复杂度,使用的额外空间仅为两个栈的大小。

在实际应用中,栈和队列都有各自的应用场景。栈常用于处理需要后进先出(LIFO)顺序的问题,如函数调用栈、括号匹配等。而队列则常用于处理需要先进先出(FIFO)顺序的问题,如任务调度、消息队列等。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KeepCoding♪Toby♪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值