232. Implement Queue using Stacks

xiaoxiao2021-02-28  13

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 toppeek/pop from topsize, 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).

看了一下去年一刷的做法 看着比较简单 

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

主要操作都在push里面了 比如现在stack里面从栈顶到栈底是1->2->3 push的时候 新建一个stack2 把原有的stack中的元素都push到stack2中 stack: stack2:3->2->1 然后把push的元素放入stack  stack:4 stack2:3->2->1 然后再把stack2中的元素放回到stack stack:1->2->3->4 stack2: 每次要push的时候 新建stack2作为一个中转站 把stack里面的元素放到stack2 然后把最新的元素放到stack 然后再把stack2的元素放回 有点像汉诺塔 如果放入的元素是从小到大的话 那就非常符合了 每次push的时候 把stack柱子上的圆盘,也就是元素放到stack2上,然后把最大盘子放到stack上,然后再从stack2上把盘子转移回stack 这样就保证了始终是最大圆盘在底部  也就符合了queue得特征 FIFO(first in first out) 看似简单 但实际上时间复杂度比较高 solution中对这种解法的时间复杂度进行了分析 Each element, with the exception of the newly arrived, is pushed and popped twice. The last inserted element is popped and pushed once. Therefore this gives 4 n + 2operations where n is the queue size. 时间复杂度算O(n) 但是操作是4n+2次

对比一下solution

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; }

push pop的平均时间复杂度都是O(1) 对于pop操作的时间复杂度分析:

Complexity Analysis

Time complexity: Amortized O(1)O(1), Worst-case O(n)O(n).

In the worst case scenario when stack s2 is empty, the algorithm pops nn elements from stack s1 and pushes nnelements to s2, where nn is the queue size. This gives 2n2n operations, which is O(n)O(n). But when stack s2 is not empty the algorithm has O(1)O(1) time complexity. So what does it mean by Amortized O(1)O(1)? Please see the next section on Amortized Analysis for more information.

Space complexity : O(1)O(1).

Amortized Analysis

Amortized analysis gives the average performance (over time) of each operation in the worst case. The basic idea is that a worst case operation can alter the state in such a way that the worst case cannot occur again for a long time, thus amortizing its cost.

Consider this example where we start with an empty queue with the following sequence of operations applied:

push_1, push_2, \ldots, push_n, pop_1,pop_2 \ldots, pop_npush1,push2,,pushn,pop1,pop2,popn

The worst case time complexity of a single pop operation is O(n)O(n). Since we have nn pop operations, using the worst-case per operation analysis gives us a total of O(n^2)O(n2) time.

However, in a sequence of operations the worst case does not occur often in each operation - some operations may be cheap, some may be expensive. Therefore, a traditional worst-case per operation analysis can give overly pessimistic bound. For example, in a dynamic array only some inserts take a linear time, though others - a constant time.

In the example above, the number of times pop operation can be called is limited by the number of push operations before it. Although a single pop operation could be expensive, it is expensive only once per n times (queue size), when s2 is empty and there is a need for data transfer between s1 and s2. Hence the total time complexity of the sequence is : n (for push operations) + 2*n (for first pop operation) + n - 1 ( for pop operations) which is O(2*n)O(2n).This gives O(2n/2n)O(2n/2n) = O(1)O(1) average time per operation.

转载请注明原文地址: https://www.6miu.com/read-1750057.html

最新回复(0)