LetCode系列之「用栈实现队列」

232. 用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列的支持的所有操作(pushpoppeekempty

  • 直接上最优解

两个栈分别充当不同的角色:

  • 一个只负责存(push)
  • 一个只负责取(pop)

假设负责存和取的栈分别为s1s2

  • 当存的时候只需要直接pushs1即可,不需要考虑其他的情况
  • 当取的时候,肯定直接从s2中取,如果s2不为空有则直接s2.pop(),如果s2为空呢,就只能将s1中的所有元素添加到s2中,然后再执行s2.pop()
  • peek()的实现逻辑与pop()相同,只是peek()只是拿到栈顶元素而不取出而已。

如下图阐述了整个过程:
在这里插入图片描述

Java实现如下:

class MyQueue {
    
    
  
  	private Stack<Integer> s1;
    private Stack<Integer> s2;

    /** Initialize your data structure here. */
    public MyQueue() {
    
    
        s1 = new Stack<>();
        s2 = new Stack<>();
    }
    
    /** Push element x to the back of queue. */
    public int push(int x) {
    
    
        return s1.push(x); 
    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
    
    
        if(s2.isEmpty()){
    
    
            while(!s1.isEmpty()){
    
    
                s2.push(s1.pop());
            }
        } 
        return s2.pop();
    }
    
    /** Get the front element. */
    public int peek() {
    
    
        if(s2.isEmpty()){
    
    
            while(!s1.isEmpty()){
    
    
                s2.push(s1.pop());
            }
        } 
        return s2.peek();
    }
    
    /** Returns whether the queue is empty. */
    public boolean empty() {
    
    
        return s1.isEmpty() && s2.isEmpty();
    }
}

时间复杂度分析

  1. push()

显而易见,push操作的时间复杂度为O(1)

  1. pop()

pop()的时间复杂度为 O(1),有人可能会问,不是最坏情况下需要考虑将s1中所有的元素取出然后push到s2中吗?下面就来分析一下,pop()的时间复杂度为什么是 O(1)而不是O(n)

pop()操作的时间复杂度比较有意思,某一次pop()操作的时间复杂度取决于s2是否为空:

  • 如果s2为不为空,则pop()操作的时间复杂度为O(1)
  • 如果s2为为空,需要将s1中所有的元素取出然后push到s2中,然后再执行s2.pop(),则pop()操作的时间复杂度取决于s1中元素的个数。可以发现,只要执行一次「将s1中所有的元素取出然后push到s2中」的操作,那么,之后s2.pop()操作的时间复杂度都为O(1),直到s2再次为空。

摊还分析

摊还分析给出了所有操作的平均性能。摊还分析的核心在于,最坏情况下的操作一旦发生了一次,那么在未来很长一段时间都不会再次发生,这样就会均摊每次操作的代价。

单次s2.pop()操作最坏情况下的时间复杂度为 O(n)。考虑到要做 n 次出队操作,如果用最坏情况下的时间复杂度来计算的话,那么所有操作的时间复杂度为 O(n^2)。然而,在一系列的操作中,最坏情况不可能每次都发生,在一系列的的s2.pop()操作中,某一次操作为 O(n)的时间内复杂度摊还给后续的时间复杂度为O(1)的操作。

某次的出队操作最多可以执行的次数跟它之前执行过入队操作的次数有关。虽然一次出队操作代价可能很大,但是每 n 次入队才能产生这么一次代价为 n 的 出队 操作。因此所有操作的总时间复杂度为:n(所有的入队操作产生) + 2 * n(第一次出队操作产生) + n - 1(剩下的出队操作产生), 所以实际时间复杂度为 O(2*n)。于是我们可以得到每次操作的平均时间复杂度为 O(2n/2n)=O(1)

  1. peek()

同理,peek()的时间复杂度也为O(1)

  1. empty()

empty()的时间复杂度为O(1)

猜你喜欢

转载自blog.csdn.net/weixin_44471490/article/details/110531475