问题描述
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 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)。