Problem
Design and implement a stack using a priority queue. The stack should support the following operations:
push(x)
: Push elementx
onto the stack.pop()
: Removes the element on top of the stack and returns it.top()
: Get the top element.isEmpty()
: Return whether the stack is empty.
Recall that a heap has the following operations:
push(item)
, which adds a new key to the heappop()
, which removes and returns the max value of the heap
Examples
Example 1:
|
|
Solution
Method 1 - Using PriorityQueue
A stack follows the Last In First Out (LIFO) principle. In contrast, a priority queue or heap orders elements by their priority.
To emulate stack behavior using a priority queue, we can assign each element a priority that increases with every push
operation. This priority serves as a timestamp or sequence number, ensuring the most recently added element is given the highest priority.
By using a priority queue where each element is associated with a priority level that increases as we push elements, we can simulate stack behavior:
- Each push operation will associate the pushed element with an increasing priority (timestamp or sequence number).
- The pop operation will then remove the element with the highest priority (most recent timestamp).
- The top operation will peek at the element with the highest priority.
Here is the approach:
- Assigning Priority: Each
push
operation increments a counter, which acts as the priority for the pushed element. The priority helps determine the order of elements in the priority queue. - Heap Structure: Utilize a max-heap to store pairs of (priority, element). If a max-heap is not available, a min-heap with negative priorities can serve the same purpose.
- Push Operation: Insert the element into the priority queue with the current counter as its priority, then increment the counter.
- Pop Operation: Remove and return the element with the highest priority.
- Top Operation: Peek at the element with the highest priority.
- isEmpty Operation: Check if the priority queue is empty.
graph TD A[Push 1, 2, 3] --> |Enqueue Operation increases priority by 1 for each push| B[(val = 3, priority = 3 \n val = 2, priority = 2 \n val = 1, priority = 1)] B --> |Dequeue Operation pops element by highest priority| C[3,2,1] style B fill:#96f,stroke:#333,stroke-width:4px; style C fill:#f96,stroke:#333,stroke-width:4px;
Here is the pseudocode:
|
|
Code
|
|
|
|
Complexity
- ⏰ Time complexity:
O(log n)
, forpush
,pop
, andtop
, wheren
is the number of elements in the stack - 🧺 Space complexity:
O(n)
, due to storing elements and their priorities in the heap.