题目:
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
9.7补充:原来LeetCode上也有一样的题目,栈实现队列和队列实现栈都有。
这道题只要想到解法就完全没有其他问题了,关键就在于解法。刚开始一直以为要让其中一个栈就像队列一样的顺序排列,所以想不到解决方案,看了剑指的思路才发现只是需要模拟出入队列,并不要求两个栈中的一个是队列的顺序,这样这道题就变简单了。每次入队时,把元素push进stack1,而当出队时,如果stack2为空,则将stack1的所有元素从后到前push进stack2并pop掉,然后stack2的栈顶元素就是要被pop的元素了,而stack2本身就有元素的话,就是它的栈顶元素了。
C++代码:
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if (stack2.empty()) {
while (!stack1.empty()) {
stack2.push(stack1.top());
stack1.pop();
}
}
int pop_num = stack2.top();
stack2.pop();
return pop_num;
}
private:
stack stack1;
stack stack2;
};
9.7 刷LC刷回来结果发现现在想的没之前那么灵活了,没想到stack2的栈顶其实就是队列的头,就从stack1复制到stack2,再从stack2返回到stack1,结果发现时间和空间都是100%:(但是这代码还是别看了)
class MyQueue {
public:
/** Initialize your data structure here. */
stack stack1;
stack stack2;
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
stack1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
while (stack1.size() != 1) {
stack2.push(stack1.top());
stack1.pop();
}
int result = stack1.top();
stack1.pop();
while (stack2.size() != 0) {
stack1.push(stack2.top());
stack2.pop();
}
return result;
}
/** Get the front element. */
int peek() {
while (stack1.size() != 1) {
stack2.push(stack1.top());
stack1.pop();
}
int result = stack1.top();
while (stack2.size() != 0) {
stack1.push(stack2.top());
stack2.pop();
}
return result;
}
/** Returns whether the queue is empty. */
bool empty() {
return (stack1.empty() && stack2.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();
*/
在discussion里看到只需要用一个栈就实现的做法:https://leetcode.com/problems/implement-queue-using-stacks/discuss/64196/0-ms-C%2B%2B-solution-using-one-Stack-w-explanation.,留着以后看。
2020.4.13回来写java了,果然又忘了做法。LC不知道为啥必须把new写在外面而不能放在constructor里,很迷惑。然后发现之前一直没有好好分析过时间复杂度,现在上了683了感觉学到了挺多。下面这个代码的写法,对于push是O(1)(很显然),而pop是amortized O(1)。什么叫做amortized呢,就是虽然最坏情况下(也就是s2为空的情况下)需要进行2n次操作(从s1里pop出来和push进s2),但是这个发生一次以后,在后面的n - 1次pop中都不需要再这么复杂了,也就是相当于平均每次pop就是2次操作,所以就是amortized O(1)。最开始接触到amortized是在用array来实现stack/queue中resize的时候,因为每次capacity不够的时候就扩一倍,需要把旧的array中的元素复制到新的array,所以也是amortized O(1)的操作。在我下面这个写法中,peek也是amortized O(1),但是似乎可以通过加一个front的field在class里来实现真正O(1)的peek。empty显然也是O(1)。
Runtime: 0 ms, faster than 100.00% of Java online submissions for Implement Queue using Stacks.
Memory Usage: 37 MB, less than 6.25% of Java online submissions for Implement Queue using Stacks.
class MyQueue {
Stack s1 = new Stack<>();
Stack s2 = new Stack<>();
/** Initialize your data structure here. */
public MyQueue() {
}
/** Push element x to the back of queue. */
public void push(int x) {
s1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.pop());
}
}
return s2.pop();
}
/** Get the front element. */
public int peek() {
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.pop());
}
}
return s2.peek();
}
/** Returns whether the queue is empty. */
public boolean 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();
* boolean param_4 = obj.empty();
*/
solutions里还有一种解法是O(n)的push和O(1)的pop,是在push的时候就把先stack1中的元素pop到stack2中,然后把要push的元素也push到stack2中,然后把整个stack2 pop回stack1中,stack1的顺序就是queue那样的了,top元素是最先push进stack的,所以pop的时候就直接O(1)即可。
另外,剑指还提出了采用两个队列实现一个栈。实现栈要复杂一些,刚开始我们往queue1中push进元素,当需要pop元素时,由于如果要把queue1的全部push进queue2的话相当于顺序没变,所以只需要把queue1除了队尾元素以外的所有元素pop掉并push进queue2,queue1剩下的队尾元素就是要pop的元素了。如果要接着pop的话,还得重新重复前面的动作,把queue2中的元素push回queue1,相当于是每次pop元素的时候都要把不为空的队列中的元素push进空队列中,不像前面用stack实现queue一样每次都只需要找stack2的元素,这里就需要两个queue交替使用了。接下来如果还要继续push的话,也是得往不为空的队列中push。这种解法的时间复杂度push是O(1),pop是O(n)(没得amortized了,直接每次都两边倒)。9.7到lc上测试了一下,运行时间4ms,53.96%,空间8.9M,60%:(C++)
class MyStack {
public:
/** Initialize your data structure here. */
queue queue1;
queue queue2;
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
if (queue1.empty() && queue2.empty()) {
queue1.push(x);
}
else {
if (queue1.empty()) {
queue2.push(x);
}
else {
queue1.push(x);
}
}
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int pop_num;
if (queue2.empty()) {
while (queue1.size() != 1) {
queue2.push(queue1.front());
queue1.pop();
}
pop_num = queue1.front();
queue1.pop();
}
else {
while (queue2.size() != 1) {
queue1.push(queue2.front());
queue2.pop();
}
pop_num = queue2.front();
queue2.pop();
}
return pop_num;
}
/** Get the top element. */
int top() {
int pop_num;
if (queue2.empty()) {
while (queue1.size() != 1) {
queue2.push(queue1.front());
queue1.pop();
}
pop_num = queue1.front();
queue2.push(pop_num);
queue1.pop();
}
else {
while (queue2.size() != 1) {
queue1.push(queue2.front());
queue2.pop();
}
pop_num = queue2.front();
queue1.push(pop_num);
queue2.pop();
}
return pop_num;
}
/** Returns whether the stack is empty. */
bool empty() {
return (queue1.empty() && queue2.empty());
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
上面的做法是两个queue一个为空一个不为空,来回倒,所以需要每次操作都要判断谁为空,下面这个做法就每次pop完把q2里的东西放回q1(再次回来写java的时候看到,直接把q1和q2 swap一下,就不需要第二个while loop了,但是一定要注意不能直接q1 = q2,这样的话q1和q2都指向了同一个object,会导致下一次操作在while中从q1 pop出来加入q2的时候又给加回去了),所以push的时候只需要直接操作q1即可。刚开始被坑了一点点就是用queue实现stack的时候不能像之前用stack实现queue那样,top的时候只需要把queue1的size循环到1,留着最后一个元素在queue1里不处理,而应该把它丢到queue2里,再重新放回来,不然的话queue2放回来的东西就会跑到queue1剩下的那个元素的后面,顺序就不对了。
C++代码:
class MyStack {
public:
/** Initialize your data structure here. */
queue queue1;
queue queue2;
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
queue1.push(x);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
while (queue1.size() != 1) {
queue2.push(queue1.front());
queue1.pop();
}
int result = queue1.front();
queue1.pop();
while (queue2.size() != 0) {
queue1.push(queue2.front());
queue2.pop();
}
return result;
}
/** Get the top element. */
int top() {
while (queue1.size() != 1) {
queue2.push(queue1.front());
queue1.pop();
}
int result = queue1.front();
queue2.push(result); // Be aware!
queue1.pop(); // Be aware!
while (queue2.size() != 0) {
queue1.push(queue2.front());
queue2.pop();
}
return result;
}
/** Returns whether the stack is empty. */
bool empty() {
return (queue1.empty() && queue2.empty());
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
Java:
Runtime: 0 ms, faster than 100.00% of Java online submissions for Implement Stack using Queues.
Memory Usage: 36.7 MB, less than 6.67% of Java online submissions for Implement Stack using Queues.
class MyStack {
Queue q1 = new LinkedList<>();
Queue q2 = new LinkedList<>();
/** Initialize your data structure here. */
public MyStack() {
}
/** Push element x onto stack. */
public void push(int x) {
q1.add(x);
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
while (q1.size() > 1) {
int top = q1.remove();
q2.add(top);
}
int result = q1.remove();
Queue temp = q1; // must use a temp, otherwise q1 and q2 point to the same obejct
q1 = q2;
q2 = temp;
return result;
}
/** Get the top element. */
public int top() {
while (q1.size() > 1) {
q2.add(q1.remove());
}
int result = q1.remove();
q2.add(result);
Queue temp = q1; // must use a temp, otherwise q1 and q2 point to the same obejct
q1 = q2;
q2 = temp;
return result;
}
/** Returns whether the stack is empty. */
public boolean empty() {
return q1.isEmpty() && q2.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
还有一种push O(n),pop O(1)的做法,就是在push的时候就按stack的方式存储,先把要push的元素放进q2,然后把q1中的元素push进q2,最后再swap一下q1和q2即可。
Runtime: 0 ms, faster than 100.00% of Java online submissions for Implement Stack using Queues.
Memory Usage: 37.3 MB, less than 6.67% of Java online submissions for Implement Stack using Queues.
class MyStack {
Queue q1 = new LinkedList<>();
Queue q2 = new LinkedList<>();
/** Initialize your data structure here. */
public MyStack() {
}
/** Push element x onto stack. */
public void push(int x) {
q2.add(x);
while (!q1.isEmpty()) {
q2.add(q1.remove());
}
Queue temp = q1; // must use a temp, otherwise q1 and q2 point to the same obejct
q1 = q2;
q2 = temp;
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return q1.remove();
}
/** Get the top element. */
public int top() {
return q1.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return q1.isEmpty() && q2.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
最后一种方法其实跟上一种差不多,但是只需要一个queue,push的时候先把要push的元素push到queue中,然后依次把queue的front pop到queue的末尾,里面存的就是一个stack了,其他pop啥的就直接对队头操作就好了,代码写起来非常简洁。
C++结果:4ms,53.96%,空间8.8M,93.33%:
class MyStack {
public:
/** Initialize your data structure here. */
queue q;
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
q.push(x);
for (int i = 0; i < q.size() - 1; i++) {
q.push(q.front());
q.pop();
}
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int result = q.front();
q.pop();
return result;
}
/** Get the top element. */
int top() {
return q.front();
}
/** Returns whether the stack is empty. */
bool empty() {
return q.empty();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
Java:
Runtime: 0 ms, faster than 100.00% of Java online submissions for Implement Stack using Queues.
Memory Usage: 37.1 MB, less than 6.67% of Java online submissions for Implement Stack using Queues.
为啥用java这时间空间都一样啊orz
class MyStack {
Queue q1 = new LinkedList<>();
/** Initialize your data structure here. */
public MyStack() {
}
/** Push element x onto stack. */
public void push(int x) {
q1.add(x);
while (q1.peek() != x) {
q1.add(q1.remove());
}
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return q1.remove();
}
/** Get the top element. */
public int top() {
return q1.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return q1.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/