• 队列题目:用栈实现队列


    题目

    标题和出处

    标题:用栈实现队列

    出处:232. 用栈实现队列

    难度

    4 级

    题目描述

    要求

    请你仅使用两个栈实现一个先进先出的队列。实现的队列应该支持普通队列的全部操作( push \texttt{push} push pop \texttt{pop} pop peek \texttt{peek} peek empty \texttt{empty} empty)。

    实现 MyQueue \texttt{MyQueue} MyQueue 类:

    • void   push(int   x) \texttt{void push(int x)} void push(int x) 将元素 x \texttt{x} x 推到队尾。
    • int   pop() \texttt{int pop()} int pop() 移除并返回队首元素。
    • int   peek() \texttt{int peek()} int peek() 返回队首元素。
    • boolean   empty() \texttt{boolean empty()} boolean empty() 如果队列是空的,返回 true \texttt{true} true;否则,返回 false \texttt{false} false

    注意:

    • 你只能使用栈的基本操作,只有在栈顶添加元素、在栈顶查看和删除元素、返回元素个数和判断是否为空的操作是有效的。
    • 你所使用的语言也许不支持栈。你可以使用列表或者双端队列来模拟一个栈,只要所有的操作都是标准的栈操作即可。

    示例

    示例 1:

    输入:
    ["MyQueue",   "push",   "push",   "peek",   "pop",   "empty"] \texttt{["MyQueue", "push", "push", "peek", "pop", "empty"]} ["MyQueue", "push", "push", "peek", "pop", "empty"]
    [[],   [1],   [2],   [],   [],   []] \texttt{[[], [1], [2], [], [], []]} [[], [1], [2], [], [], []]
    输出:
    [null,   null,   null,   1,   1,   false] \texttt{[null, null, null, 1, 1, false]} [null, null, null, 1, 1, false]
    解释:
    MyQueue   myQueue   =   new   MyQueue(); \texttt{MyQueue myQueue = new MyQueue();} MyQueue myQueue = new MyQueue();
    myQueue.push(1); \texttt{myQueue.push(1);} myQueue.push(1);
    myQueue.push(2); \texttt{myQueue.push(2);} myQueue.push(2);
    myQueue.peek(); \texttt{myQueue.peek();} myQueue.peek(); // 返回 1 \texttt{1} 1
    myQueue.pop(); \texttt{myQueue.pop();} myQueue.pop(); // 返回 1 \texttt{1} 1
    myQueue.empty(); \texttt{myQueue.empty();} myQueue.empty(); // 返回 False \texttt{False} False

    数据范围

    • 1 ≤ x ≤ 9 \texttt{1} \le \texttt{x} \le \texttt{9} 1x9
    • 最多调用 100 \texttt{100} 100 push \texttt{push} push pop \texttt{pop} pop peek \texttt{peek} peek empty \texttt{empty} empty
    • 每次调用 pop \texttt{pop} pop peek \texttt{peek} peek 都是有效的

    进阶

    你能否实现每个操作均摊时间复杂度为 O(1) \texttt{O(1)} O(1) 的队列?换句话说,执行 n \texttt{n} n 个操作的总时间复杂度为 O(n) \texttt{O(n)} O(n),即使其中一个操作可能花费较长时间。

    解法

    思路和算法

    由于队列的特点是先进先出,因此使用栈实现队列时,需要维护栈内的元素顺序,在出队操作和查看队首元素操作时可以定位到最先添加的元素。

    如果不考虑时间复杂度的限制,一种可行的解法是,每次入队操作时维护栈内的元素顺序,使得从栈顶到栈底的元素符合添加顺序。具体做法是,使用两个栈,其中 stack 1 \textit{stack}_1 stack1 的作用是存储元素, stack 2 \textit{stack}_2 stack2 的作用是辅助操作,入队操作如下:

    1. stack 1 \textit{stack}_1 stack1 的全部元素依次出栈并入栈到 stack 2 \textit{stack}_2 stack2

    2. 将待添加的元素入栈到 stack 1 \textit{stack}_1 stack1

    3. stack 2 \textit{stack}_2 stack2 的全部元素依次出栈并入栈到 stack 1 \textit{stack}_1 stack1

    完成上述操作后, stack 2 \textit{stack}_2 stack2 为空, stack 1 \textit{stack}_1 stack1 的栈底元素是新添加的元素, stack 1 \textit{stack}_1 stack1 内的其余元素和顺序不变。因此上述操作的效果是在 stack 1 \textit{stack}_1 stack1 的栈底增加了新添加的元素, stack 1 \textit{stack}_1 stack1 的栈顶和栈底分别对应队首和队尾。

    由于每次入队操作都维护了 stack 1 \textit{stack}_1 stack1 内的元素顺序,因此出队操作和查看队首元素操作的时间复杂度是 O ( 1 ) O(1) O(1),只要对 stack 1 \textit{stack}_1 stack1 的栈顶元素执行相应的操作即可(移除并返回元素,或者返回元素但不移除)。

    判断队列是否为空操作只要判断 stack 1 \textit{stack}_1 stack1 是否为空即可。

    上述做法虽然可以做到出队操作、查看队首元素操作和判断队列是否为空操作的时间复杂度是 O ( 1 ) O(1) O(1),但是当队列内有 n n n 个元素时,入队操作需要 2 n 2n 2n 次出栈操作和 2 n + 1 2n + 1 2n+1 次入栈操作,因此入队操作的时间复杂度是 O ( n ) O(n) O(n)

    为了做到均摊时间复杂度是 O ( 1 ) O(1) O(1),需要使用优化的做法,将 stack 1 \textit{stack}_1 stack1 用于入队操作,将 stack 2 \textit{stack}_2 stack2 用于出队操作和查看队首元素操作。

    对于入队操作,将待添加的元素入栈到 stack 1 \textit{stack}_1 stack1

    对于出队操作和查看队首元素操作,做法如下:

    1. 判断 stack 2 \textit{stack}_2 stack2 是否为空,如果为空,则将 stack 1 \textit{stack}_1 stack1 的全部元素依次出栈并入栈到 stack 2 \textit{stack}_2 stack2

    2. stack 2 \textit{stack}_2 stack2 的栈顶元素执行相应的操作(移除并返回元素,或者返回元素但不移除)。

    判断队列是否为空操作需要判断 stack 1 \textit{stack}_1 stack1 stack 2 \textit{stack}_2 stack2 是否都为空。

    优化的做法中,入队操作和判断队列是否为空操作的时间复杂度是 O ( 1 ) O(1) O(1),对于出队操作和查看队首元素操作,由于每个元素最多在两个栈都入栈和出栈各一次,因此均摊时间复杂度是 O ( 1 ) O(1) O(1)

    证明

    对于优化的做法的正确性证明,需要证明出队操作和查看队首元素操作返回的元素是最先入队的元素。

    1. 由于入队操作时将待添加的元素入栈到 stack 1 \textit{stack}_1 stack1,因此 stack 1 \textit{stack}_1 stack1 从栈底到栈顶的元素顺序符合入队顺序。

    2. 出队操作和查看队首元素操作时,如果 stack 2 \textit{stack}_2 stack2 为空,则将 stack 1 \textit{stack}_1 stack1 的全部元素依次出栈并入栈到 stack 2 \textit{stack}_2 stack2,此时 stack 2 \textit{stack}_2 stack2 内的元素顺序和 stack 1 \textit{stack}_1 stack1 内的元素顺序相反, stack 2 \textit{stack}_2 stack2 的全部元素中,栈顶元素是最先入队的元素。

    3. 由于所有的元素在入栈到 stack 2 \textit{stack}_2 stack2 之前首先会入栈到 stack 1 \textit{stack}_1 stack1,因此 stack 2 \textit{stack}_2 stack2 内的元素的入队时间一定比 stack 1 \textit{stack}_1 stack1 的入队时间早, stack 2 \textit{stack}_2 stack2 的栈顶元素是队列的全部元素中最先入队的元素。

    代码

    class MyQueue {
        Deque<Integer> stack1;
        Deque<Integer> stack2;
    
        public MyQueue() {
            stack1 = new ArrayDeque<Integer>();
            stack2 = new ArrayDeque<Integer>();
        }
        
        public void push(int x) {
            stack1.push(x);
        }
        
        public int pop() {
            preparePop();
            return stack2.pop();
        }
        
        public int peek() {
            preparePop();
            return stack2.peek();
        }
        
        public boolean empty() {
            return stack1.isEmpty() && stack2.isEmpty();
        }
    
        private void preparePop() {
            if (stack2.isEmpty()) {
                while (!stack1.isEmpty()) {
                    stack2.push(stack1.pop());
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    复杂度分析

    • 时间复杂度:构造方法的时间复杂度是 O ( 1 ) O(1) O(1),入队和判断队列是否为空操作的时间复杂度都是 O ( 1 ) O(1) O(1),出队和查看队首元素操作的均摊时间复杂度都是 O ( 1 ) O(1) O(1)。每个元素最多在两个栈都入栈和出栈各一次。

    • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是队列内元素个数。需要使用栈存储元素。

  • 相关阅读:
    Servlet--Response响应对象
    ubuntu20.04下安装nc
    游戏d3dcompiler_43.dll缺失怎么修复,分享5个快速有效的修复方法
    使用kubesphere图形界面创建一个devops的CI/CD流程
    科研学习|研究方法——Python计量Logit模型
    ProAntd+react+ts表格行点击高亮+表格联动
    CET4汉译英part
    springboot启动原理最终版
    WinApp自动化测试之工具的选择
    如何评价GPT-4o?
  • 原文地址:https://blog.csdn.net/stormsunshine/article/details/121048170