Stacks are a fundamental data structure that follow the Last-In-First-Out (LIFO) principle. This principle is exactly opposite of First-In-First-Out (FIFO) principle on Queue data structure is based on. While stacks can be implemented directly using an array or a linked list, they can also be constructed using two queues.
Implementing a stack with queues involves maintaining two queues, where one queue is used to store the elements, while the other queue is used to reverse the order of the elements when popping from the stack. Below is a basic program which demonstrates implementing a stack data structure using two queue objects:
from queue import Queue
class Stack:
def __init__(self):
self.q1 = Queue()
self.q2 = Queue()
def push(self, item):
"""
Adds an item to the top of the stack.
* First add the current item to Queue q2
* Next add all the items present in Queue q1 to q2
* Now Swap q1 and q2
* Now q1 contains recently added item at the beginning
* and oldest added element at the ending
* So when get is called on queue, recently added element
* will be returned, similar to Stack ADT
"""
self.q2.put(item)
while not self.q1.empty():
self.q2.put(self.q1.get())
self.q1, self.q2 = self.q2, self.q1
def pop(self):
"""
Removes and returns the top item from the stack.
"""
if self.q1.empty():
raise IndexError("Stack is empty")
return self.q1.get()
def peek(self):
"""
Returns the top item from the stack without removing it.
"""
if self.q1.empty():
raise IndexError("Stack is empty")
return self.q1.queue[0]
def is_empty(self):
"""
Returns True if the stack is empty, False otherwise.
"""
return self.q1.empty()
# Let's now test the Stack implementation
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print("Popping last element: ", stack.pop()) # Output: 3
print("Peeking last element: ", stack.peek()) # Output: 2
print("Popping last element: ", stack.pop()) # Output: 2
print(stack.is_empty()) # Output: False, since item 1 is still in stack
#include <queue>
#include <stdexcept>
#include <iostream>
class Stack {
public:
Stack() {
// Initialize two queues
q1 = std::queue<int>();
q2 = std::queue<int>();
}
void push(int item) {
/**
* Adds an item to the top of the stack.
* * First add the current item to Queue q2
* * Next add all the items present in Queue q1 to q2
* * Now Swap q1 and q2
* * Now q1 contains recently added item at the beginning
* * and oldest added element at the ending
* * So when get is called on queue, recently added element
* * will be returned, similar to Stack ADT
*/
q2.push(item);
while (!q1.empty()) {
q2.push(q1.front());
q1.pop();
}
std::swap(q1, q2);
}
int pop() {
/**
* Removes and returns the top item from the stack.
*/
if (q1.empty()) {
throw std::runtime_error("Stack is empty");
}
int top_element = q1.front();
q1.pop();
return top_element;
}
int peek() {
/**
* Returns the top item from the stack without removing it.
*/
if (q1.empty()) {
throw std::runtime_error("Stack is empty");
}
return q1.front();
}
bool is_empty() {
/**
* Returns True if the stack is empty, False otherwise.
*/
return q1.empty();
}
private:
std::queue<int> q1, q2;
};
int main() {
Stack stack;
stack.push(1);
stack.push(2);
stack.push(3);
std::cout << "Popping last element: " << stack.pop() << std::endl; // Output: 3
std::cout << "Peeking last element: " << stack.peek() << std::endl; // Output: 2
std::cout << "Popping last element: " << stack.pop() << std::endl; // Output: 2
std::cout << std::boolalpha << stack.is_empty() << std::endl; // Output: false, since item 1 is still in stack
return 0;
}
// Import the Queue class
class Queue {
constructor() {
this.items = [];
}
// Add an item to the end of the queue
enqueue(item) {
this.items.push(item);
}
// Remove and return the first item in the queue
dequeue() {
if (this.items.length === 0) {
return "Underflow";
}
return this.items.shift();
}
// Return the first item in the queue without removing it
peek() {
return this.items[0];
}
// Check if the queue is empty
isEmpty() {
return this.items.length === 0;
}
}
// Implement the Stack class using two Queues
class Stack {
constructor() {
this.q1 = new Queue();
this.q2 = new Queue();
}
// Add an item to the top of the stack
push(item) {
// Add the current item to Queue q2
this.q2.enqueue(item);
// Add all the items present in Queue q1 to q2
while (!this.q1.isEmpty()) {
this.q2.enqueue(this.q1.dequeue());
}
// Swap q1 and q2
[this.q1, this.q2] = [this.q2, this.q1];
}
// Remove and return the top item from the stack
pop() {
if (this.q1.isEmpty()) {
throw new Error("Stack is empty");
}
return this.q1.dequeue();
}
// Return the top item from the stack without removing it
peek() {
if (this.q1.isEmpty()) {
throw new Error("Stack is empty");
}
return this.q1.peek();
}
// Check if the stack is empty
isEmpty() {
return this.q1.isEmpty();
}
}
// Test the Stack implementation
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log("Popping last element: ", stack.pop()); // Output: 3
console.log("Peeking last element: ", stack.peek()); // Output: 2
console.log("Popping last element: ", stack.pop()); // Output: 2
console.log(stack.isEmpty()); // Output: false, since item 1 is still in stack
import java.util.LinkedList;
import java.util.Queue;
public class Main {
private static class Stack {
private Queue<Integer> q1;
private Queue<Integer> q2;
public Stack() {
this.q1 = new LinkedList<>();
this.q2 = new LinkedList<>();
}
/**
* Adds an item to the top of the stack.
* - First add the current item to Queue q2
* - Next add all the items present in Queue q1 to q2
* - Now Swap q1 and q2
* - Now q1 contains recently added item at the beginning
* - and oldest added element at the ending
* - So when get is called on queue, recently added element
* - will be returned, similar to Stack ADT
*/
public void push(int item) {
q2.offer(item);
while (!q1.isEmpty()) {
q2.offer(q1.poll());
}
Queue<Integer> temp = q1;
q1 = q2;
q2 = temp;
}
/**
* Removes and returns the top item from the stack.
*/
public int pop() {
if (q1.isEmpty()) {
throw new IndexOutOfBoundsException("Stack is empty");
}
return q1.poll();
}
/**
* Returns the top item from the stack without removing it.
*/
public int peek() {
if (q1.isEmpty()) {
throw new IndexOutOfBoundsException("Stack is empty");
}
return q1.peek();
}
/**
* Returns True if the stack is empty, False otherwise.
*/
public boolean isEmpty() {
return q1.isEmpty();
}
}
public static void main(String[] args) {
Stack stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
System.out.println("Popping last element: " + stack.pop()); // Output: 3
System.out.println("Peeking last element: " + stack.peek()); // Output: 2
System.out.println("Popping last element: " + stack.pop()); // Output: 2
System.out.println(stack.isEmpty()); // Output: false, since item 1 is still in stack
}
}
Here’s how the implementation works:
The Stack class has two queue objects, q1 and q2, which are used to implement the stack.
The push() method adds an item to the top of the stack. It does this by first adding the item to q2, and then moving all the items from q1 to q2. Finally, it swaps the references to q1 and q2 so that q1 now contains the updated stack.
The pop() method removes and returns the top item from the stack. It does this by checking if q1 is empty, and if so, raising an error. Otherwise, it returns the item at the front of q1.
The peek() method returns the top item from the stack without removing it. It does this by checking if q1 is empty, and if so, raising an error. Otherwise, it returns the item at the front of q1.
The is_empty() method returns True if the stack is empty, and False otherwise.