【剑指 Offer】09. 用两个栈实现队列

    技术2022-07-10  139

    问题描述

    用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )。

    其中:

    1 <= values <= 10000最多会对 appendTail、deleteHead 进行 10000 次调用 示例 1: 输入: ["CQueue","appendTail","deleteHead","deleteHead"] [[],[3],[],[]] 输出:[null,null,3,-1] 示例 2: 输入: ["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"] [[],[],[5],[2],[],[]] 输出:[null,-1,null,null,5,2]

     

    Java 实现

    用两个栈实现一个队列的功能,即通过两个先入后出(FILO)实现一个先入先出(FIFO),我们可以用一个栈支持插入操作,另一个栈实现删除操作。

    具体的操作为:当我们每一次执行插入元素时,直接往第一个栈 stack1 插入元素,根据栈的结构特性,栈底是第一个插入也是下一个待删除的元素,栈顶是最后插入的元素。当我们想执行删除操作时,需要将 stack1 的元素依次取出,取到最后才能访问到该删除的元素,但同时我们也想保持元素插入的顺序,这时就可以用上第二个栈 stack2。假设第一次执行删除时,stack2位空,则先把 stack1 的元素依次弹出并插入到 stack2 中,直至 stack1完全清空,此时 stack2 的栈顶即为下一个待删除的元素,直至栈底依次为接下来待删除的元素。此后,每次执行删除操作只需从 stack2 弹出元素即可。

    当 stack2 栈内存在元素的时候,删除与插入操作分别对 stack2 与 stack1进行操作,且两个栈之间相互独立。即删除操作只在 stack2 进行,插入操作只在 stack1 进行。在 stack2 清空之前,stack1 维持后一批插入的元素及其插入顺序。每次当且仅当 stack2 为空,且需要进行删除操作时,才从 stack1 向 stack2 转移数据。如果转移后 stack2 仍为空,即此时无元素存在,则返回 -1。

    class CQueue { private Stack<Integer> stack1; private Stack<Integer> stack2; public CQueue() { stack1 = new Stack<>(); stack2 = new Stack<>(); } public void appendTail(int value) { stack1.push(value); } public int deleteHead() { if (!stack2.isEmpty()) { return stack2.pop(); } else { while (!stack1.isEmpty()) { stack2.push(stack1.pop()); } if (stack2.isEmpty()) { return -1; } else { return stack2.pop(); } } } }

    复杂度分析

    空间复杂度:维护两个栈进行存储,因此为 O(n)。时间复杂度:插入操作,明显为 O(1)。删除操作看似为 O(n),但经过仔细考虑会发现,每次执行删除时只从 stack2 弹出一次,即便 stack2 为空,需要从 stack1 将所有元素都转移一遍,但这个过程同样被均摊到每一个元素的插入和弹出过程中,因此删除操作的时间复杂度为 O(1)。
    Processed: 0.010, SQL: 9