1. 程式人生 > >[LeetCode]225. Implement Stack using Queues

[LeetCode]225. Implement Stack using Queues

文章目錄

題目

https://leetcode.com/problems/implement-stack-using-queues/description/

Implement the following operations of a stack using queues.
push(x) -- Push element x onto stack.
pop() -- Removes the element on top of the stack.
top() -- Get the top element.
empty() -- Return whether the stack is empty.
Example:
MyStack stack = new MyStack();
stack.push(1);
stack.push(2);  
stack.top();   // returns 2
stack.pop();   // returns 2
stack.empty(); // returns false

官方Solution

This article is for beginners. It introduces the following ideas: Stack, Queue.

Approach #1 (Two Queues, push - O(1), pop - O(n) )

Intuition
Stack is LIFO (last in - first out) data structure, in which elements are added and removed from the same end, called top. In general stack is implemented using array or linked list, but in the current article we will review a different approach for implementing stack using queues. In contrast queue is FIFO (first in - first out) data structure, in which elements are added only from the one side - rear and removed from the other - front. In order to implement stack using queues, we need to maintain two queues q1 and q2. Also we will keep top stack element in a constant memory.

Push

The new element is always added to the rear of queue q1 and it is kept as top stack element
在這裡插入圖片描述

private Queue<Integer> q1 = new LinkedList<>();
private Queue<Integer> q2 = new LinkedList<>();
private int top;
// Push element x onto stack.
public void push(int x) {
    q1.add(x);
    top = x;
}

Complexity Analysis
Time complexity : O(1). Queue is implemented as linked list and add operation has O(1) time complexity.
Space complexity : O(1)

Pop

We need to remove the element from the top of the stack. This is the last inserted element in q1. Because queue is FIFO (first in - first out) data structure, the last inserted element could be removed only after all elements, except it, have been removed. For this reason we need to maintain additional queue q2, which will serve as a temporary storage to enqueue the removed elements from q1. The last inserted element in q2 is kept as top. Then the algorithm removes the last element in q1. We swap q1 with q2 to avoid copying all elements from q2 to q1.完成pop後要交換q1和q2.
在這裡插入圖片描述

// Removes the element on top of the stack.
public void pop() {
    while (q1.size() > 1) {
        top = q1.remove();//從佇列中取出所有前面的元素
        q2.add(top);//新增到q2
    }
    q1.remove();
    Queue<Integer> temp = q1;
    q1 = q2;
    q2 = temp;
}

Complexity Analysis
Time complexity : O(n). The algorithm dequeues n elements from q1 and enqueues n - 1 elements to q2, where nn is the stack size. This gives 2n - 1operations.
Space complexity : O(1).

Approach #2 (Two Queues, push - O(n), pop - O(1) )

Push

The algorithm inserts each new element to queue q2 and keep it as the top element. In case queue q1 is not empty (there are elements in the stack), we remove all elements from q1 and add them to q2. In this way the new inserted element (top element in the stack) will be always positioned at the front of q2. We swap q1 with q2 to avoid copying all elements from q2 to q1.
在這裡插入圖片描述

public void push(int x) {
    q2.add(x);
    top = x;
    while (!q1.isEmpty()) {                
        q2.add(q1.remove());
    }
    Queue<Integer> temp = q1;
    q1 = q2;
    q2 = temp;
}

Complexity Analysis
Time complexity : O(n). The algorithm removes n elements from q1 and inserts n+1 elements to q2, where n is the stack size. This gives 2n+1 operations. The operations add and remove in linked lists has O(1) complexity.
Space complexity : O(1).

Pop

The algorithm dequeues an element from queue q1 and keeps front element of q1 as top.
在這裡插入圖片描述

// Removes the element on top of the stack.
public void pop() {
    q1.remove();
    if (!q1.isEmpty()) {
        top = q1.peek();
    }
}

Complexity Analysis
Time complexity : O(1).
Space complexity : O(1).
In both approaches empty and top operations have the same implementation.

Empty

Queue q1 always contains all stack elements, so the algorithm checks q1 size to return if the stack is empty.

// Return whether the stack is empty.
public boolean empty() {
    return q1.isEmpty();
}

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

Approach #3 (One Queue, push - O(n), pop - O(1) )

The mentioned above two approaches have one weakness, they use two queues. This could be optimized as we use only one queue, instead of two.只用一個佇列完成。

Push

When we push an element into a queue, it will be stored at back of the queue due to queue’s properties. But we need to implement a stack, where last inserted element should be in the front of the queue, not at the back. To achieve this we can invert the order of queue elements when pushing a new element.新增元素後反轉佇列。
在這裡插入圖片描述

private LinkedList<Integer> q1 = new LinkedList<>();
// Push element x onto stack.
public void push(int x) {
    q1.add(x);
    int sz = q1.size();
    while (sz > 1) {
        q1.add(q1.remove());
        sz--;
    }
}

Complexity Analysis
Time complexity : O(n). The algorithm removes n elements and inserts n+1 elements to q1 , where n is the stack size. This gives 2n+1 operations. The operations add and remove in linked lists has O(1) complexity.
Space complexity : O(1).

Pop

The last inserted element is always stored at the front of q1 and we can pop it for constant time.

// Removes the element on top of the stack.
public void pop() {
    q1.remove();
}

Complexity Analysis
Time complexity : O(1).
Space complexity : O(1).

Empty

Queue q1 contains all stack elements, so the algorithm checks if q1 is empty.
// Return whether the stack is empty.

public boolean empty() {
    return q1.isEmpty();
}

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

Top

The top element is always positioned at the front of q1. Algorithm return it.

// Get the top element.
public int top() {
    return q1.peek();
}

Time complexity : O(1).
Space complexity : O(1).
Analysis written by: @elmirap.

總結

1、用兩個佇列可以模擬一個棧,要麼在push做特殊處理,要麼在pop時做特殊處理
2、用一個佇列也可以實現一個棧,每次新增元素後,需要把這個元素放入佇列頭,為此要把前面的元素都出隊再入隊。
3、佇列的基本操作:
宣告一個佇列:private Queue q1 = new LinkedList<>();
基本操作:add()、remove()、peek()
4、時間複雜度:入棧出棧至少有一個是O(n)
空間複雜度:要藉助兩個佇列則為O(n),一個佇列則為O(1)。