Implement a First-In-First-Out (FIFO) queue using a single stack. The implemented queue should support the following operations: push, pop, peek, and isEmpty.
Implement the StackQueue class:
void push(int x): Adds element x to the end of the queue.
int pop(): Removes and returns the front element of the queue.
int peek(): Returns the front element of the queue without removing it.
boolean isEmpty(): Returns true if the queue is empty, false otherwise.
Input:
["StackQueue", "push", "push", "pop", "peek", "isEmpty"]
[[], [4], [8], [], [], []]
Output:[null, null, null, 4, 8, false]
Explanation:
StackQueue queue = new StackQueue();
queue.push(4);
queue.push(8);
queue.pop(); // returns 4
queue.peek(); // returns 8
queue.isEmpty(); // returns false
Input:
["StackQueue", "isEmpty"]
[[]]
Output: [null, true]
Explanation:
StackQueue queue = new StackQueue();
queue.isEmpty(); // returns true
Input:
["StackQueue", "push", "pop", "isEmpty"]
[[], [6], [], []]
To better visualize this problem imagine a line of people waiting to get tickets for a movie. The first person who arrives at the counter is the first one to get the ticket, just like in a queue. Now, if there are two large boxes, one for placing and another for reversing the order of tickets, the first box can be used to collect tickets from people in the order they arrive. When it's time to issue the tickets, they can be transferred to the second box to reverse their order, ensuring the first person to arrive is the first one to get the ticket, simulating the behavior of a queue using stacks.
The goal is to implement a queue using stacks, which naturally follow Last In, First Out (LIFO) order. To achieve the First In, First Out (FIFO) order of a queue, the idea is to use two stacks. By pushing elements onto one stack and then transferring them to another stack before popping, the order of elements is reversed twice. This ensures that the first element pushed onto the stack is the first one to be popped, thus mimicking a queue's behavior.
stack1
and stack2
.stack1
to stack2
.stack1
.stack2
to stack1
.stack1
.stack1
without removing it.stack1
.#include <bits/stdc++.h>
using namespace std;
// Queue implementation using stack
class StackQueue {
private:
stack <int> st1, st2;
public:
// Empty Constructor
StackQueue () {
}
// Method to push elements in the queue
void push(int x) {
/* Pop out elements from the first stack
and push on top of the second stack */
while (!st1.empty()) {
st2.push(st1.top());
st1.pop();
}
// Insert the desired element
st1.push(x);
/* Pop out elements from the second stack
and push back on top of the first stack */
while (!st2.empty()) {
st1.push(st2.top());
st2.pop();
}
}
// Method to pop element from the queue
int pop() {
// Edge case
if (st1.empty()) {
cout << "Stack is empty";
return -1; // Representing empty stack
}
// Get the top element
int topElement = st1.top();
st1.pop(); // Perform the pop operation
return topElement; // Return the popped value
}
// Method to get the front element from the queue
int peek() {
// Edge case
if (st1.empty()) {
cout << "Stack is empty";
return -1; // Representing empty stack
}
// Return the top element
return st1.top();
}
// Method to find whether the queue is empty
bool isEmpty() {
return st1.empty();
}
};
int main() {
StackQueue q;
// List of commands
vector<string> commands = {"StackQueue", "push", "push",
"pop", "peek", "isEmpty"};
// List of inputs
vector<vector<int>> inputs = {{}, {4}, {8}, {}, {}, {}};
for (int i = 0; i < commands.size(); ++i) {
if (commands[i] == "push") {
q.push(inputs[i][0]);
cout << "null ";
} else if (commands[i] == "pop") {
cout << q.pop() << " ";
} else if (commands[i] == "peek") {
cout << q.peek() << " ";
} else if (commands[i] == "isEmpty") {
cout << (q.isEmpty() ? "true" : "false") << " ";
} else if (commands[i] == "StackQueue") {
cout << "null ";
}
}
return 0;
}
import java.util.*;
class StackQueue {
private Stack<Integer> st1, st2;
// Constructor
public StackQueue() {
st1 = new Stack<>();
st2 = new Stack<>();
}
// Method to push elements in the queue
public void push(int x) {
/* Pop out elements from the first stack
and push on top of the second stack */
while (!st1.isEmpty()) {
st2.push(st1.pop());
}
// Insert the desired element
st1.push(x);
/* Pop out elements from the second stack
and push back on top of the first stack */
while (!st2.isEmpty()) {
st1.push(st2.pop());
}
}
// Method to pop element from the queue
public int pop() {
// Edge case
if (st1.isEmpty()) {
System.out.println("Stack is empty");
return -1; // Representing empty stack
}
// Get the top element
int topElement = st1.pop(); // Perform the pop operation
return topElement; // Return the popped value
}
// Method to get the front element from the queue
public int peek() {
// Edge case
if (st1.isEmpty()) {
System.out.println("Stack is empty");
return -1; // Representing empty stack
}
// Return the top element
return st1.peek();
}
// Method to find whether the queue is empty
public boolean isEmpty() {
return st1.isEmpty();
}
}
class Main {
public static void main(String[] args) {
StackQueue q = new StackQueue();
// List of commands
String[] commands = {"StackQueue", "push", "push",
"pop", "peek", "isEmpty"};
// List of inputs
int[][] inputs = {{}, {4}, {8}, {}, {}, {}};
for (int i = 0; i < commands.length; i++) {
if (commands[i].equals("push")) {
q.push(inputs[i][0]);
System.out.print("null ");
} else if (commands[i].equals("pop")) {
System.out.print(q.pop() + " ");
} else if (commands[i].equals("peek")) {
System.out.print(q.peek() + " ");
} else if (commands[i].equals("isEmpty")) {
System.out.print((q.isEmpty() ? "true" : "false") + " ");
} else if (commands[i].equals("StackQueue")) {
System.out.print("null ");
}
}
}
}
class StackQueue:
def __init__(self):
# Constructor
self.st1 = []
self.st2 = []
# Method to push elements in the queue
def push(self, x):
'''Pop out elements from the first stack
and push on top of the second stack'''
while self.st1:
self.st2.append(self.st1.pop())
# Insert the desired element
self.st1.append(x)
'''Pop out elements from the second stack
and push back on top of the first stack'''
while self.st2:
self.st1.append(self.st2.pop())
# Method to pop element from the queue
def pop(self):
# Edge case
if not self.st1:
print("Stack is empty")
return -1 # Representing empty stack
# Get the top element
top_element = self.st1.pop() # Perform the pop operation
return top_element # Return the popped value
# Method to get the front element from the queue
def peek(self):
# Edge case
if not self.st1:
print("Stack is empty")
return -1 # Representing empty stack
# Return the top element
return self.st1[-1]
# Method to find whether the queue is empty
def is_empty(self):
return not self.st1
if __name__ == "__main__":
q = StackQueue()
# List of commands
commands = ["StackQueue", "push", "push", "pop", "peek", "isEmpty"]
# List of inputs
inputs = [[], [4], [8], [], [], []]
for i in range(len(commands)):
if commands[i] == "push":
q.push(inputs[i][0])
print("null", end=" ")
elif commands[i] == "pop":
print(q.pop(), end=" ")
elif commands[i] == "peek":
print(q.peek(), end=" ")
elif commands[i] == "isEmpty":
print("true" if q.is_empty() else "false", end=" ")
elif commands[i] == "StackQueue":
print("null", end=" ")
class StackQueue {
constructor() {
// Constructor
this.st1 = [];
this.st2 = [];
}
// Method to push elements in the queue
push(x) {
/* Pop out elements from the first stack
and push on top of the second stack */
while (this.st1.length) {
this.st2.push(this.st1.pop());
}
// Insert the desired element
this.st1.push(x);
/* Pop out elements from the second stack
and push back on top of the first stack */
while (this.st2.length) {
this.st1.push(this.st2.pop());
}
}
// Method to pop element from the queue
pop() {
// Edge case
if (!this.st1.length) {
console.log("Stack is empty");
return -1; // Representing empty stack
}
// Get the top element
return this.st1.pop(); // Perform the pop operation
}
// Method to get the front element from the queue
peek() {
// Edge case
if (!this.st1.length) {
console.log("Stack is empty");
return -1; // Representing empty stack
}
// Return the top element
return this.st1[this.st1.length - 1];
}
// Method to find whether the queue is empty
isEmpty() {
return this.st1.length === 0;
}
}
const q = new StackQueue();
// List of commands
const commands = ["StackQueue", "push", "push", "pop", "peek", "isEmpty"];
// List of inputs
const inputs = [[], [4], [8], [], [], []];
for (let i = 0; i < commands.length; i++) {
if (commands[i] === "push") {
q.push(inputs[i][0]);
console.log("null");
} else if (commands[i] === "pop") {
console.log(q.pop());
} else if (commands[i] === "peek") {
console.log(q.peek());
} else if (commands[i] === "isEmpty") {
console.log(q.isEmpty() ? "true" : "false");
} else if (commands[i] === "StackQueue") {
console.log("null");
}
}
Imagine a line of customers at a busy bank where there are two counters to manage the queue. The first counter (input stack) receives the customers as they arrive and registers them in order. As the queue grows, customers are simply added to this first counter. When it’s time to serve a customer, the bank uses a second counter (output stack) to reverse the order of customers. By transferring all waiting customers from the first counter to the second, the bank can ensure that the first customer who arrived is served first. This way, the initial order is preserved even though the bank uses a two-step process.
The concept of using two stacks to create a queue relies on the idea of reversing the order of elements twice to achieve the desired First In, First Out (FIFO) behavior. Stacks naturally follow Last In, First Out (LIFO) order, meaning the last element added is the first one removed. By using two stacks, elements can be added to one stack and then moved to another to reverse their order. When it’s time to remove an element, transferring elements to the second stack ensures that the first element added to the first stack becomes the first one removed from the second stack. This double reversal effectively mimics the behavior of a queue using the LIFO nature of stacks.
inputStack
and outputStack
.inputStack
. This operation is efficient and always takes O(1) time.outputStack
is empty, move all elements from the inputStack
to the outputStack
. This reversal of order ensures that the oldest element is on top of the outputStack
.outputStack
. This represents the oldest element in the queue.outputStack
is empty, move all elements from the inputStack
to the outputStack
to access the oldest element.outputStack
without removing it. This gives the element that has been in the queue the longest.#include <bits/stdc++.h>
using namespace std;
class StackQueue {
public:
stack<int> input, output;
// Initialize your data structure here
StackQueue() {}
// Push element x to the back of queue
void push(int x) {
input.push(x);
}
// Removes the element from in front of queue and returns that element
int pop() {
// Shift input to output if output is empty
if (output.empty()) {
while (!input.empty()) {
output.push(input.top());
input.pop();
}
}
// If queue is still empty, return -1 (or throw an error if preferred)
if (output.empty()) {
cout << "Queue is empty, cannot pop." << endl;
return -1;
}
int x = output.top();
output.pop();
return x;
}
// Get the front element
int peek() {
// Shift input to output if output is empty
if (output.empty()) {
while (!input.empty()) {
output.push(input.top());
input.pop();
}
}
// If queue is still empty, return -1 (or throw an error if preferred)
if (output.empty()) {
cout << "Queue is empty, cannot peek." << endl;
return -1;
}
return output.top();
}
// Returns true if the queue is empty, false otherwise
bool isEmpty() {
return input.empty() && output.empty();
}
};
int main() {
StackQueue q;
q.push(3);
q.push(4);
cout << "The element popped is " << q.pop() << endl;
q.push(5);
cout << "The front of the queue is " << q.peek() << endl;
cout << "Is the queue empty? " << (q.isEmpty() ? "Yes" : "No") << endl;
cout << "The element popped is " << q.pop() << endl;
cout << "The element popped is " << q.pop() << endl;
cout << "Is the queue empty? " << (q.isEmpty() ? "Yes" : "No") << endl;
return 0;
}
import java.util.*;
class StackQueue {
private Stack<Integer> input, output;
// Initialize your data structure here
public StackQueue() {
input = new Stack<>();
output = new Stack<>();
}
// Push element x to the back of queue
public void push(int x) {
input.push(x);
}
// Removes the element from in front of queue and returns that element
public int pop() {
// Shift input to output if output is empty
if (output.isEmpty()) {
while (!input.isEmpty()) {
output.push(input.pop());
}
}
// If queue is still empty, return -1 (or throw an error if preferred)
if (output.isEmpty()) {
System.out.println("Queue is empty, cannot pop.");
return -1;
}
return output.pop();
}
// Get the front element
public int peek() {
// Shift input to output if output is empty
if (output.isEmpty()) {
while (!input.isEmpty()) {
output.push(input.pop());
}
}
// If queue is still empty, return -1 (or throw an error if preferred)
if (output.isEmpty()) {
System.out.println("Queue is empty, cannot peek.");
return -1;
}
return output.peek();
}
// Returns true if the queue is empty, false otherwise
public boolean isEmpty() {
return input.isEmpty() && output.isEmpty();
}
}
class Main {
public static void main(String[] args) {
StackQueue q = new StackQueue();
q.push(3);
q.push(4);
System.out.println("The element popped is " + q.pop());
q.push(5);
System.out.println("The front of the queue is " + q.peek());
System.out.println("Is the queue empty? " + (q.isEmpty() ? "Yes" : "No"));
System.out.println("The element popped is " + q.pop());
System.out.println("The element popped is " + q.pop());
System.out.println("Is the queue empty? " + (q.isEmpty() ? "Yes" : "No"));
}
}
class StackQueue:
def __init__(self):
# Initialize your data structure here
self.input = [] # Stack to push elements
self.output = [] # Stack to simulate FIFO order
# Push element x to the back of queue
def push(self, x: int):
self.input.append(x)
# Removes the element from in front of queue and returns that element
def pop(self) -> int:
# Shift input to output if output is empty
if not self.output:
while self.input:
self.output.append(self.input.pop())
# If queue is still empty, return -1 (or throw an error if preferred)
if not self.output:
print("Queue is empty, cannot pop.")
return -1
return self.output.pop()
# Get the front element
def peek(self) -> int:
# Shift input to output if output is empty
if not self.output:
while self.input:
self.output.append(self.input.pop())
# If queue is still empty, return -1 (or throw an error if preferred)
if not self.output:
print("Queue is empty, cannot peek.")
return -1
return self.output[-1]
# Returns true if the queue is empty, false otherwise
def isEmpty(self) -> bool:
return not self.input and not self.output
# Main function to test the StackQueue implementation
if __name__ == "__main__":
q = StackQueue()
q.push(3)
q.push(4)
print("The element popped is", q.pop())
q.push(5)
print("The front of the queue is", q.peek())
print("Is the queue empty?", "Yes" if q.isEmpty() else "No")
print("The element popped is", q.pop())
print("The element popped is", q.pop())
print("Is the queue empty?", "Yes" if q.isEmpty() else "No")
class StackQueue {
constructor() {
// Initialize your data structure here
this.input = []; // Stack to push elements
this.output = []; // Stack to simulate FIFO order
}
// Push element x to the back of queue
push(x) {
this.input.push(x);
}
// Removes the element from in front of queue and returns that element
pop() {
// Shift input to output if output is empty
if (this.output.length === 0) {
while (this.input.length > 0) {
this.output.push(this.input.pop());
}
}
// If queue is still empty, return -1 (or throw an error if preferred)
if (this.output.length === 0) {
console.log("Queue is empty, cannot pop.");
return -1;
}
return this.output.pop();
}
// Get the front element
peek() {
// Shift input to output if output is empty
if (this.output.length === 0) {
while (this.input.length > 0) {
this.output.push(this.input.pop());
}
}
// If queue is still empty, return -1 (or throw an error if preferred)
if (this.output.length === 0) {
console.log("Queue is empty, cannot peek.");
return -1;
}
return this.output[this.output.length - 1];
}
// Returns true if the queue is empty, false otherwise
isEmpty() {
return this.input.length === 0 && this.output.length === 0;
}
}
// Main function to test the StackQueue implementation
(function() {
let q = new StackQueue();
q.push(3);
q.push(4);
console.log("The element popped is", q.pop());
q.push(5);
console.log("The front of the queue is", q.peek());
console.log("Is the queue empty?", q.isEmpty() ? "Yes" : "No");
console.log("The element popped is", q.pop());
console.log("The element popped is", q.pop());
console.log("Is the queue empty?", q.isEmpty() ? "Yes" : "No");
})();
Q: Why use recursion to implement pop() and peek()?
A: Since a stack removes elements in LIFO order, recursion helps reach the first inserted element (bottom of the stack), retrieve it, and restore the order.
Q: Can we make pop() more efficient?
A: Yes, using two stacks would allow pop() to be O(1) amortized, but this implementation requires only one stack.
Q: How would you optimize this solution using two stacks?
A: Use one stack for enqueue (push()) and another for dequeue (pop() and peek()). When pop() is called, transfer elements from the push stack to the pop stack only if the pop stack is empty.
Q: How would you modify this to return the size of the queue?
A: Maintain a counter that updates when push() or pop() is called.