LeetCode 232. Implement Queue using Stacks && 225. Implement Stack using Queues && 剑指9. 用两个栈实现队列

题目

用两个栈来实现一个队列,完成队列的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();
 */

 

你可能感兴趣的:(剑指Offer,LeetCode)