思路如下

无论是「用栈实现队列」还是说「用队列实现栈」,其实思路都是类似的。

都可以通过用两个 栈或者队列 来解决。

现在我们创建两个栈,分别是 outin,用作处理「输出」和「输入」两个操作。

其实就是两个栈来回的「倒腾」

而对于「何时倒腾」决定了是 O(n) 解法 还是 均摊 O(1) 解法

1 先来说 O(n) 解法

我们创建两个栈,分别是 out 和 in:

  • in 用作处理输入操作 push(),使用 in 时需确保 out 为空
  • out 用作处理输出操作 pop() 和 peek(),使用 out 时需确保 in 为空
class MyQueue {
    Deque<Integer> out, in;
    public MyQueue() {
        in = new ArrayDeque<>();
        out = new ArrayDeque<>();
    }
    
    public void push(int x) {
        while (!out.isEmpty()) in.addLast(out.pollLast());
        in.addLast(x);
    }
    
    public int pop() {
        while (!in.isEmpty()) out.addLast(in.pollLast());
        return out.pollLast();
    }
    
    public int peek() {
        while (!in.isEmpty()) out.addLast(in.pollLast());
        return out.peekLast();
    }
    
    public boolean empty() {
        return out.isEmpty() && in.isEmpty();
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

2 再来说均摊 O(1) 解法

事实上,我们不需要在每次的「入栈」和「出栈」操作中都进行「倒腾」。

我们只需要保证,输入的元素总是跟在前面的输入元素的后面就行啦,而输出元素总是最早输入的那个元素即可

我们可以通过调整「倒腾」的时机来确保满足上述要求,但又不需要发生在每一次操作中:

  • 即只有在「输出栈」为空的时候,才发生一次性的「倒腾」
//java的版本
class MyQueue {
    Deque<Integer> out, in;
    public MyQueue() {
        in = new ArrayDeque<>();
        out = new ArrayDeque<>();
    }
    
    public void push(int x) {
        in.addLast(x);
    }
    
    public int pop() {
        if (out.isEmpty()) {
            while (!in.isEmpty()) out.addLast(in.pollLast());
        }
        return out.pollLast();
    }
    
    public int peek() {
        if (out.isEmpty()) {
            while (!in.isEmpty()) out.addLast(in.pollLast());
        }
        return out.peekLast();
    }
    
    public boolean empty() {
        return out.isEmpty() && in.isEmpty();
    }
}
  • 时间复杂度:pop() 和 peek()两个操作都是均摊,所以是O(1)
  • 空间复杂度:O(n)

你可能没听过均摊复杂度

3 我说明下

我们可以用另外一个例子来理解「均摊复杂度」,你就明白了,大家都知道「哈希表」的底层是通过数组来实现的。

正常情况下,我们要算元素在哈希桶的位置,然后放入哈希桶里面,复杂度为 O(1),我们可以通过简单的“拉链法”搭配「头插法」方式来解决哈希冲突的问题。

但是当某次元素插入后,「哈希表」能达到扩容的阈值,那么需要对底层所使用的数组进行扩容,这个复杂度是 O(n)

很显然「扩容」操作不会发生在每一次的元素插入中,扩容的 O(n) 都会伴随着 n 次的 O(1),换句话说 O(n) 的复杂度会被均摊到每一次插入当中去,所以哈希表插入仍然还是 O(1) 的。

同样的道理,我们的「倒腾」也不是发生在每一次的「输出操作」中,而是集中发生在一次「输出栈为空」的这个时候,所以 poppeek 都是均摊复杂度为 O(1) 的操作。

由于本题的调用次数它只有 100 次,所以铁定是一个人均 100% 的算法(也就是花费 0 ms)🐶 🐶

但是我们需要对操作进行复杂度分析进行判断,而不是看时间来判断自己是不是均摊 O(1) 哈 ~