题目:
使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明:
你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/implement-queue-using-stacks
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解法一:
说明:默认情况下,实现队列最直观的方法是用链表。
push=O(n), pop=O(1)
一句话概括:
要用栈实现队列的效果,必须保持每次向栈中插入元素时,都是插入到栈的栈底。这就要求在向栈中插入元素时要先将栈中的元素全部导出到另外一个栈中,将新元素放置栈底后再将之前的元素从零一个栈中全部倒回。
使用两个栈,达到的各个操作的时间复杂度效果:
push: O(n)
pop: O(1)
peek: O(1)
empty: O(1)
代码实例:
class MyQueue {
private:
stack<int> s1;
stack<int> s2;
public:
/** Initialize your data structure here. */
MyQueue() {
}
/** Push element x to the back of queue. */ //插入元素:
void push(int x) {
while(!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
s1.push(x);
while(!s2.empty()) {
s1.push(s2.top());
s2.pop();
}
}
/** Removes the element from in front of queue and returns that element. */ //弹出元素:
int pop() {
int top = s1.top();
s1.pop();
return top;
}
/** Get the front element. */ //访问队首元素:
int peek() {
return s1.top();
}
/** Returns whether the queue is empty. */ //判空:
bool empty() {
return s1.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
解法二:摊还时间复杂度:
push: O(1)
pop: 摊还时间复杂度:O(1);最坏时间复杂度:O(n)
peek: O(1)
empty: O(1)
插入元素直接放到s1的栈顶(push时间复杂度O(1)),当有pop操作时,才将s1中的元素一次性搬运到s2中,实现逆序,一次性搬运的时间复杂度是O(n),之后每次pop都直接从s2中弹出元素即可,摊还时间复杂度是O(1)。
另外当s1为空时在临时变量中保存新插入元素,以便实现peek操作的O(1)复杂度。
ps:什么是摊还分析:
摊还分析给出了所有操作的平均性能。摊还分析的核心在于,最坏情况下的操作一旦发生了一次,那么在未来很长一段时间都不会再次发生,这样就会均摊每次操作的代价。
class MyQueue {
private:
stack<int> s1;
stack<int> s2;
int front; //用于peek()操作:存储s1的栈底元素,在s2为空时可当做队首元素返回
public:
/** Initialize your data structure here. */
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) { //O(1)
if(s1.empty()) front = x;
s1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
if(s2.empty()) {
while(!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
int result = s2.top();
s2.pop();
return result;
}
/** Get the front element. */
int peek() {
if(s2.empty()) return front;
return s2.top();
}
/** Returns whether the queue is empty. */
bool empty() {
return s1.empty() && s2.empty();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/