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

问题描述

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 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)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章