引言
一、双栈实现队列
我的题解
/**
* @Description: 使用两个栈 实现队列 —— 进和出
* @param {*}
* @return {*}
* @notes: 关键:s1设置为队尾, s2设置为对头
* ① 出栈的时候如果为空,则 s1入s2; 再出队。
* ② 入栈则放到s1里面。
* ③ 如果 s1和s2同时为空——出栈的时候 则为空队列 返回-1。
*/
class CQueue {
public:
CQueue() {
}
void appendTail(int value) {
s1.push(value);
}
int deleteHead() {
if(s2.empty()){
while(!s1.empty()){
int s = s1.top();
s1.pop();
s2.push(s);
}
}
if(s2.empty() && s1.empty()){
return -1;
}
// 有数值
int tmp = s2.top();
s2.pop();
return tmp;
}
private:
stack<int> s1;
stack<int> s2;
};
/**
* Your CQueue object will be instantiated and called as such:
* CQueue* obj = new CQueue();
* obj->appendTail(value);
* int param_2 = obj->deleteHead();
*/
更多的功能
/**
* @Description: 使用两个栈 实现队列 —— 进和出
* @param {*}
* @return {*}
* @notes: 关键:s1设置为队尾, s2设置为对头
* ① 出栈的时候如果为空,则 s1入s2; 再出队。
* ② 入栈则放到s1里面。
* ③ 如果 s1和s2同时为空——出栈的时候 则为空队列 返回-1。
*/
class MyQueue {
public:
/** Initialize your data structure here. */
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
s1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
if(s2.empty()){
while(!s1.empty()){
int s = s1.top();
s1.pop();
s2.push(s);
}
}
if(s2.empty() && s1.empty()){
return -1;
}
// 有数值
int tmp = s2.top();
s2.pop();
return tmp;
}
/** Get the front element. */
int peek() {
if(s2.empty()){
while(!s1.empty()){
int s = s1.top();
s1.pop();
s2.push(s);
}
}
if(s2.empty() && s1.empty()){
return -1;
}
// 有数值
int tmp = s2.top();
return tmp;
}
/** Returns whether the queue is empty. */
bool empty() {
if(s2.empty() && s1.empty()){
return true;
}
return false;
}
private:
stack<int> s1;
stack<int> s2;
};
至此,就用栈结构实现了一个队列,核心思想是利用两个栈互相配合。
值得一提的是,这几个操作的时间复杂度是多少呢?其他操作都是 O(1),有点意思的是peek操作,调用它时可能触发while循环,这样的话时间复杂度是 O(N),但是大部分情况下while循环不会被触发,时间复杂度是 O(1)。由于pop操作调用了peek,它的时间复杂度和peek相同。
像这种情况,可以说它们的最坏时间复杂度是 O(N),因为包含while循环,可能需要从s1往s2搬移元素。
但是它们的均摊时间复杂度是 O(1),这个要这么理解:对于一个元素,最多只可能被搬运一次,也就是说peek操作平均到每个元素的时间复杂度是 O(1)。
二、队列实现栈
使用相对暴力的方法——直接倒转前
n-1
个放回去。
stackTop
来记录队头的元素。
我的题解
class MyStack {
public:
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
q.push(x);
stackTop = x;
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
if(q.empty()){
return -1;
}
int sz = q.size()-1;
while(sz--){
int tmp = q.front();
q.pop();
q.push(tmp);
}
int tmp = q.front();
stackTop = q.back(); // 【注意这里!!】
q.pop();
return tmp;
}
/** Get the top element. */
int top() {
return stackTop;
}
/** Returns whether the stack is empty. */
bool empty() {
return q.empty();
}
private:
int stackTop;
queue<int> q;
};
三、总结
很明显,用队列实现栈的话,pop 操作时间复杂度是 O(N),其他操作都是 O(1)。
个人认为,用队列实现栈没啥亮点,但是 用双栈实现队列是值得学习的。
出栈顺序本来就和入栈顺序相反,但是从栈s1搬运元素到s2之后,s2中元素出栈的顺序就变成了队列的先进先出顺序,这个特性有点类似「负负得正」,确实不容易想到。