Published: 26 Nov 2024 | Reading Time: 6 min read
Data structures such as stacks and queues play a fundamental role in computer science. They are essential for solving a wide variety of problems, and their concepts form the foundation for understanding more advanced data structures and algorithms. These data structures are often used in situations where you need to organize and process data in a specific order. These concepts are frequently asked in coding interviews and are integral to many real-world applications.
In this article, you'll understand stacks and queues, including how they work together, which is a great way to deepen your understanding.
A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. In a stack, the last element added is the first one to be removed.
A queue follows the First In, First Out (FIFO) principle, meaning that the first element added is the first one to be removed.
Here are the programs to implement stack using the queue, using two main approaches:
In this approach, we can use a single queue to simulate the stack. When an element is pushed onto the stack, we add it to the queue, but we need to rearrange the queue to make sure the most recently added element stays at the front.
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
typedef struct Queue {
int arr[MAX_SIZE];
int front, rear;
} Queue;
void initQueue(Queue* q) {
q->front = 0;
q->rear = -1;
}
int isEmpty(Queue* q) {
return q->front > q->rear;
}
int isFull(Queue* q) {
return q->rear == MAX_SIZE - 1;
}
void enqueue(Queue* q, int value) {
if (isFull(q)) {
printf("Queue is full!\n");
return;
}
q->arr[++(q->rear)] = value;
}
int dequeue(Queue* q) {
if (isEmpty(q)) {
printf("Queue is empty!\n");
return -1;
}
return q->arr[(q->front)++];
}
void push(Queue* q, int value) {
enqueue(q, value);
int n = q->rear - q->front;
while (n--) {
enqueue(q, dequeue(q));
}
}
int pop(Queue* q) {
if (isEmpty(q)) {
printf("Stack is empty!\n");
return -1;
}
return dequeue(q);
}
int main() {
Queue q;
initQueue(&q);
push(&q, 10);
push(&q, 20);
push(&q, 30);
printf("Popped element: %d\n", pop(&q));
printf("Popped element: %d\n", pop(&q));
printf("Popped element: %d\n", pop(&q));
return 0;
}
#include <iostream>
#include <queue>
using namespace std;
class Stack {
private:
queue<int> q;
public:
void push(int value) {
q.push(value);
int n = q.size();
for (int i = 0; i < n - 1; i++) {
q.push(q.front());
q.pop();
}
}
int pop() {
if (q.empty()) {
cout << "Stack is empty!" << endl;
return -1;
}
int value = q.front();
q.pop();
return value;
}
bool isEmpty() {
return q.empty();
}
};
int main() {
Stack s;
s.push(10);
s.push(20);
s.push(30);
cout << "Popped element: " << s.pop() << endl;
cout << "Popped element: " << s.pop() << endl;
cout << "Popped element: " << s.pop() << endl;
return 0;
}
import java.util.LinkedList;
import java.util.Queue;
class StackUsingOneQueue {
private Queue<Integer> queue;
public StackUsingOneQueue() {
queue = new LinkedList<>();
}
public void push(int value) {
queue.add(value);
int n = queue.size();
for (int i = 0; i < n - 1; i++) {
queue.add(queue.poll());
}
}
public int pop() {
if (queue.isEmpty()) {
System.out.println("Stack is empty!");
return -1;
}
return queue.poll();
}
public boolean isEmpty() {
return queue.isEmpty();
}
public static void main(String[] args) {
StackUsingOneQueue stack = new StackUsingOneQueue();
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println("Popped element: " + stack.pop());
System.out.println("Popped element: " + stack.pop());
System.out.println("Popped element: " + stack.pop());
}
}
In this approach, the stack is implemented using two queues:
Push Operation:
Pop Operation:
Peek Operation:
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
typedef struct Queue {
int arr[MAX_SIZE];
int front, rear;
} Queue;
void initQueue(Queue* q) {
q->front = 0;
q->rear = -1;
}
int isEmpty(Queue* q) {
return q->front > q->rear;
}
int isFull(Queue* q) {
return q->rear == MAX_SIZE - 1;
}
void enqueue(Queue* q, int value) {
if (isFull(q)) {
printf("Queue is full!\n");
return;
}
q->arr[++(q->rear)] = value;
}
int dequeue(Queue* q) {
if (isEmpty(q)) {
printf("Queue is empty!\n");
return -1;
}
return q->arr[(q->front)++];
}
void push(Queue* q1, Queue* q2, int value) {
enqueue(q1, value);
}
int pop(Queue* q1, Queue* q2) {
if (isEmpty(q1)) {
printf("Stack is empty!\n");
return -1;
}
while (q1->front != q1->rear) {
enqueue(q2, dequeue(q1));
}
int value = dequeue(q1);
// Swap references of q1 and q2 for next operation
Queue* temp = q1;
q1 = q2;
q2 = temp;
return value;
}
int main() {
Queue q1, q2;
initQueue(&q1);
initQueue(&q2);
push(&q1, &q2, 10);
push(&q1, &q2, 20);
push(&q1, &q2, 30);
printf("Popped element: %d\n", pop(&q1, &q2));
printf("Popped element: %d\n", pop(&q1, &q2));
printf("Popped element: %d\n", pop(&q1, &q2));
return 0;
}
#include <iostream>
#include <queue>
using namespace std;
class Stack {
private:
queue<int> q1, q2;
public:
void push(int value) {
q1.push(value);
}
int pop() {
if (q1.empty()) {
cout << "Stack is empty!" << endl;
return -1;
}
while (q1.size() > 1) {
q2.push(q1.front());
q1.pop();
}
int value = q1.front();
q1.pop();
// Swap q1 and q2
swap(q1, q2);
return value;
}
bool isEmpty() {
return q1.empty();
}
};
int main() {
Stack s;
s.push(10);
s.push(20);
s.push(30);
cout << "Popped element: " << s.pop() << endl;
cout << "Popped element: " << s.pop() << endl;
cout << "Popped element: " << s.pop() << endl;
return 0;
}
import java.util.LinkedList;
import java.util.Queue;
class StackUsingTwoQueues {
private Queue<Integer> q1, q2;
public StackUsingTwoQueues() {
q1 = new LinkedList<>();
q2 = new LinkedList<>();
}
public void push(int value) {
q1.add(value);
}
public int pop() {
if (q1.isEmpty()) {
System.out.println("Stack is empty!");
return -1;
}
while (q1.size() > 1) {
q2.add(q1.poll());
}
int value = q1.poll();
// Swap references of q1 and q2
Queue<Integer> temp = q1;
q1 = q2;
q2 = temp;
return value;
}
public boolean isEmpty() {
return q1.isEmpty();
}
public static void main(String[] args) {
StackUsingTwoQueues stack = new StackUsingTwoQueues();
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println("Popped element: " + stack.pop());
System.out.println("Popped element: " + stack.pop());
System.out.println("Popped element: " + stack.pop());
}
}
Here are the advantages of implementing a stack using a queue:
Here are the disadvantages of implementing a stack using a queue:
In conclusion, implementing a stack using one queue and two queues provides a viable solution, but its practicality and efficiency depend on the specific use case and requirements. Whether you're using one queue or two queues, the process of implementing stack using a queue is about managing data flow and structuring your logic.
A stack is a data structure that follows the LIFO (Last In, First Out) principle, where elements are added to the top and removed from the top.
Yes, it is possible to implement a stack using a queue by using rotations, but it may not be the most efficient solution.
Source: NxtWave (CCBP.in)
Contact: [email protected] | +919390111761 (WhatsApp only)