ImplementQueueUsingStacks [source code]


public class ImplementQueueUsingStacks {
static
/******************************************************************************/
public class MyQueue {
    private Integer head = null;
    private Stack<Integer> stack;
    private int size = 0;

    /** Initialize your data structure here. */
    public MyQueue() {
        stack = new Stack<Integer>();
    }

    /** Push element x to the back of queue. */
    public void push(int x) {
        if (size++ == 0) head = x;
        stack.push(x);
    }

    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        if (size - 1 == 0) head = null;
        Stack<Integer> tmp = new Stack<>();
        for (int i = 0; i < size - 1; i++) {
            tmp.push(stack.pop());
        }
        int res = stack.pop();
        size--;
        for (int i = 0; i < size; i++) {
            if (i == 0) head = tmp.peek();
            stack.push(tmp.pop());
        }
        return res;
    }

    /** Get the front element. */
    public int peek() {
        return head;
    }

    /** Returns whether the queue is empty. */
    public boolean empty() {
        return size == 0;
    }
}
/******************************************************************************/

    public static void main(String[] args) {
        ImplementQueueUsingStacks.MyQueue tester = new ImplementQueueUsingStacks.MyQueue();
        tester.push(1);
        tester.push(2);
        tester.push(3);
        System.out.println("peeking: " + tester.peek());
        for (int i = 0; i < 3; i++) System.out.println("popped: " + tester.pop());
        System.out.println(tester.empty());
    }
}

这个题目总体的思路还是非常简单的, 就是一个很基础的东西, 就是有少数的小技巧:

  • peek 需要专门用一个 instance var 来保存, 因为 peek 可能被大量使用, 所以把它单独存住可能会大幅度的提高aggregate performance.
  • size也是专门用一个 int 来保存, 而不是依赖 Stack 本身的 API; 这个在 Sedgwick 的书上已经见识过了, 这个是一个可以大幅提升速度的技巧, 却很容易实现;

可以看到上面最后一个注释里面给出的, pop 和 peek 返回的都是 int, 所以这里估计不用考虑 generic;

这个问题上唯一一个调了半天的一个 bug, 就是这个:

for (int i = 0; i < tmp.size(); i++) {  
    if (i == 0) head = tmp.peek();  
    stack.push(tmp.pop());  
}

这个刚开始死活半天都想不出来, 后来其实这个的原因就跟之前写 BFS 的时候, 每一个 level 的 iteration 里面要先专门把 queue 的 size 存下来一样; 因为你 tmp 的 size 是一直在改变的;
借此总结的一个教训就是, 不要在 loop 的 header 里面使用除了 array 以外的其他的 collection 的 size API 作为 range; 这个是一个新手很容易上当的地方;

Integer的 null 是可以直接 print, 不需要自己再专门判断一下;

最后的速度是94ms (83%), 还算可以;


editorial给出了两种思路, 第一种是 push 的时候完成我这里的这个O(n) 的操作(用一个 temp Stack 完成一个把 newest 放到 bottom 的操作), 这样 pop 就可以做到O(1);
第二种思路就是我这里的思路, 把 push 做成O(1);
看起来好像没有什么神奇的地方, 不过他这个 editorial 给出一个观点, 就是第二种思路, 其实可以做到最后的amortized performance是O(1), 这个是一个很有意思的事情, 而且事实上这种问题感觉是很容易在面试的时候被问到的. 我当时也不知道为什么就选择让 push 成为O(1).
好像我这个做法并不能做到O(1)的 amortized;

回头重新看了一下, 他这个方法实际上比我的复杂, 他是维护两个 stack: s1 and s2; 然后当第一次 pop 的时候, 直接s1的全都倒序 push 到 s2里面去; 然后 pop 的默认 stack 是 s2, 只有当 s2空了以后, 才重新做一次 s1倒灌. 他这样做确实是可以做到O(1)的 amortized 的; 很聪明;

private Stack<Integer> s1 = new Stack<>();  
private Stack<Integer> s2 = new Stack<>();  

// Push element x to the back of queue.  
public void push(int x) {  
    if (s1.empty())  
        front = x;  
    s1.push(x);  
}  
// Removes the element from in front of queue.  
public void pop() {  
    if (s2.isEmpty()) {  
        while (!s1.isEmpty())  
            s2.push(s1.pop());  
    }  
    s2.pop();      
}  
// Return whether the queue is empty.  
public boolean empty() {  
    return s1.isEmpty() && s2.isEmpty();  
}  
// Get the front element.  
public int peek() {  
    if (!s2.isEmpty()) {  
            return s2.peek();  
    }  
    return front;  
}

不过他这个代码写的好像有点问题; pop 的返回值写了个 void;


Problem Description

Implement the following operations of a queue using stacks.

push(x) -- Push element x to the back of queue.
pop() -- Removes the element from in front of queue.
peek() -- Get the front element.
empty() -- Return whether the queue is empty.
Notes:
You must use only standard operations of a stack -- which means only push to top, peek/pop from top, size, and is empty operations are valid.
Depending on your language, stack may not be supported natively. You may simulate a stack by using a list or deque (double-ended queue), as long as you use only standard operations of a stack.
You may assume that all operations are valid (for example, no pop or peek operations will be called on an empty queue).
Difficulty:Easy
Category:Algorithms
Acceptance:36.40%
Contributor: LeetCode
Companies
microsoft bloomberg
Related Topics
stack design
Similar Questions
Implement Stack using Queues

/**

  • 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();
    */

results matching ""

    No results matching ""