思路
队列讲究的时先进先出(FIFO)原则,我们的栈是先进后出,最先入栈的会在栈的底部,这与我们的原则相悖,那么如何解决这个问题呢?可以采取双栈的设计,一个栈instack负责入队操作,一个栈outstack负责出队操作,比如入队一个元素,会进入到instack的底部,如果我们再希望他出队的话,只需要将他取出来压入到outstack里面,这个过程中他上面的元素在outstack中是依次往下的,要出队的元素在outstack最上面,outstack的出栈顺序和出队顺序一样的。这样就达到了入队出队的效果。
实现
#include <stack>
#include <iostream>
template <typename T>
class MyQueue {
private:
std::stack<T> inStack; // 输入栈,负责入队操作
std::stack<T> outStack; // 输出栈,负责出队操作
// 将输入栈数据转移到输出栈(核心操作)
void transfer() {
while (!inStack.empty()) {
outStack.push(inStack.top());
inStack.pop();
}
}
public:
MyQueue() {}
// 入队操作:时间复杂度 O(1)
void push(T x) {
inStack.push(x);
}
// 出队操作:摊还时间复杂度 O(1)
T pop() {
if (outStack.empty()) {
transfer();
}
T val = outStack.top();
outStack.pop();
return val;
}
// 查看队首元素:摊还时间复杂度 O(1)
T peek() {
if (outStack.empty()) {
transfer();
}
return outStack.top();
}
// 判断队列是否为空:时间复杂度 O(1)
bool empty() {
return inStack.empty() && outStack.empty();
}
// 获取队列元素数量:时间复杂度 O(1)
size_t size() {
return inStack.size() + outStack.size();
}
};
/*
* 复杂度分析:
* - push: O(1) 直接压栈
* - pop/peek:
* - 最佳情况 O(1)(当 outStack 不为空时)
* - 最差情况 O(n)(需要转移整个 inStack)
* - 摊还分析:每个元素最多经历两次栈操作(压入 inStack,弹出 inStack,压出 outStack)
* 因此 m 次操作的摊还时间复杂度为 O(m) → 单次操作摊还 O(1)
*/