Queue By Two Stacks
Queue By Two Stacks:
The Queue data structure follows the First-In-First-Out (FIFO) principle, where the element that has been in the queue for the longest time is the first one to be removed. In this implementation, we use two stacks to simulate the behavior of a queue.
Approach:
- We use one stack, called the
input_stack
, to handle the enqueue operation. - We use another stack, called the
output_stack
, to handle the dequeue operation. - When we enqueue an element, we simply push it onto the
input_stack
. - When we dequeue an element, we check if the
output_stack
is empty. If it is empty, we transfer all elements from theinput_stack
to theoutput_stack
in reversed order. Then we pop the top element from theoutput_stack
and return it.
This approach ensures that the elements in the output_stack
are in the correct order to simulate the behavior of a queue.
Example:
Here's an example of implementing a Queue using two Stacks in Python:
class Queue:
def __init__(self):
self.input_stack = []
self.output_stack = []
def enqueue(self, value):
self.input_stack.append(value)
def dequeue(self):
if not self.output_stack:
while self.input_stack:
self.output_stack.append(self.input_stack.pop())
return self.output_stack.pop()
def is_empty(self):
return len(self.input_stack) == 0 and len(self.output_stack) == 0
def size(self):
return len(self.input_stack) + len(self.output_stack)
In this code, we define the Queue
class. It has two stacks as instance variables: input_stack
and output_stack
. The enqueue
method appends elements to the input_stack
, simulating the enqueue operation. The dequeue
method checks if the output_stack
is empty. If it is, it transfers elements from the input_stack
to the output_stack
in reversed order. Then, it pops and returns the top element from the output_stack
, simulating the dequeue operation.
The is_empty
method checks if both stacks are empty, indicating an empty queue. The size
method returns the total number of elements in the queue.
Now, let's solve a question using this concept.
Question: Implement Queue using Stacks
Implement a Queue data structure using stacks. The implementation should support the following operations: push
, pop
, peek
, and empty
.
Here's the solution:
class Queue:
def __init__(self):
self.input_stack = []
self.output_stack = []
def enqueue(self, value):
self.input_stack.append(value)
def dequeue(self):
if not self.output_stack:
while self.input_stack:
self.output_stack.append(self.input_stack.pop())
return self.output_stack.pop()
def is_empty(self):
return len(self.input_stack) == 0 and len(self.output_stack) == 0
def size(self):
return len(self.input_stack) + len(self.output_stack)
In this solution, we implement the MyQueue
class, which has the same methods as the standard Queue. The push
operation appends elements to the input_stack
. The pop
operation calls the peek
method to ensure that the output_stack
is updated, then pops and returns the top element from the output_stack
. The peek
operation checks if the output_stack
is empty, transfers elements from the input_stack
if necessary, and returns the top element. The empty
operation checks if both stacks are empty.
Time and Space Complexity Analysis:
The time complexity for the enqueue operation is O(1) since we directly append elements to the input_stack
.
The time complexity for the dequeue operation is O(n), where n is the number of elements in the queue. In the worst case, when the output_stack
is empty, we need to transfer all elements from the input_stack
to the output_stack
. However, subsequent dequeue operations will be O(1) until the output_stack
becomes empty again.
The time complexity for the peek operation is O(1) since we access the top element directly.
The time complexity for the empty operation is O(1) since we only check if both stacks are empty.
The space complexity is O(n), where n is the number of elements in the queue. This is because we store all elements in either the input_stack
or the output_stack
.
Overall, this implementation provides the required queue operations with a reasonable time complexity, making it suitable for most scenarios.
Let's solve a question using the concept of implementing a Queue using two Stacks and analyze its time and space complexity.
Question: Implement Queue using Stacks
Implement a Queue data structure using stacks. The implementation should support the following operations: push
, pop
, peek
, and empty
.
Solution:
class MyQueue:
def __init__(self):
self.input_stack = []
self.output_stack = []
def push(self, x):
self.input_stack.append(x)
def pop(self):
self.peek()
return self.output_stack.pop()
def peek(self):
if not self.output_stack:
while self.input_stack:
self.output_stack.append(self.input_stack.pop())
return self.output_stack[-1]
def empty(self):
return not self.input_stack and not self.output_stack
In this solution, we implement the MyQueue
class, which utilizes two stacks: input_stack
and output_stack
. The push
operation appends elements to the input_stack
. The pop
operation calls the peek
method to ensure that the output_stack
is updated, then pops and returns the top element from the output_stack
. The peek
operation checks if the output_stack
is empty, transfers elements from the input_stack
if necessary, and returns the top element. The empty
operation checks if both stacks are empty.
Time Complexity Analysis:
The time complexity for the push
operation is O(1) since we directly append elements to the input_stack
.
The time complexity for the pop
operation is O(1) in the average case. However, in the worst case, when the output_stack
is empty, we need to transfer all elements from the input_stack
to the output_stack
, resulting in a time complexity of O(n), where n is the number of elements in the queue. Subsequent pop
operations will be O(1) until the output_stack
becomes empty again.
The time complexity for the peek
operation is O(1) in the average case. Similar to the pop
operation, the worst-case time complexity is O(n) when the output_stack
is empty and requires transferring elements from the input_stack
. Subsequent peek
operations will be O(1) until the output_stack
becomes empty again.
The time complexity for the empty
operation is O(1) since we only check if both stacks are empty.
Space Complexity Analysis:
The space complexity is O(n), where n is the number of elements in the queue. This is because we store all elements in either the input_stack
or the output_stack
.
Overall:
This implementation provides a way to simulate a queue using two stacks. While the worst-case time complexity for pop
and peek
operations is O(n), the average-case time complexity is O(1). The space complexity is reasonable at O(n). This approach is suitable for scenarios where the number of enqueue and dequeue operations is balanced and the number of elements in the queue is not excessively large.
Let's solve another question using the concept of implementing a Queue using two Stacks and analyze its time and space complexity.
Question: Implement Stack using Queues
Implement a Stack data structure using queues. The implementation should support the following operations: push
, pop
, top
, and empty
.
Solution:
class MyStack:
def __init__(self):
self.queue = []
def push(self, x):
self.queue.append(x)
size = len(self.queue)
# Move all previous elements to the end
for _ in range(size - 1):
self.queue.append(self.queue.pop(0))
def pop(self):
return self.queue.pop(0)
def top(self):
return self.queue[0]
def empty(self):
return len(self.queue) == 0
In this solution, we implement the MyStack
class, which uses a single queue to simulate a stack. The push
operation appends the new element to the end of the queue and then moves all previous elements to the end, effectively making the new element the top of the stack. The pop
operation removes and returns the front element of the queue, which corresponds to the top of the stack. The top
operation returns the front element without removing it. The empty
operation checks if the queue is empty.
Time Complexity Analysis:
The time complexity for the push
operation is O(n), where n is the number of elements in the stack. This is because, after adding the new element, we need to move all previous elements to the end of the queue. In the worst case, we iterate through all elements in the queue.
The time complexity for the pop
, top
, and empty
operations is O(1) since we directly access the front element of the queue or check if the queue is empty.
Space Complexity Analysis:
The space complexity is O(n), where n is the number of elements in the stack. This is because we store all elements in the queue.
Overall:
This implementation provides a way to simulate a stack using a single queue. The push
operation has a time complexity of O(n) due to the need to move elements, while the other operations have a time complexity of O(1). The space complexity is reasonable at O(n). This approach is suitable for scenarios where the number of push operations is balanced with the number of pop operations, and the number of elements in the stack is not excessively large.