Two fundamental linear data structures that control how we add and remove elements. Master LIFO and FIFO principles through interactive visualization.
Last In, First Out. Like a pile of plates. Operations happen only at the top.
First In, First Out. Like a line at a store. Fair processing order.
A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. The last element added is the first one to be removed. Think of it as a stack of books or plates.
Add an element to the top of the stack. O(1) time complexity.
Remove the top element from the stack. O(1) time complexity.
View the top element without removing it. O(1) time complexity.
class Stack {
constructor() {
this.items = [];
}
// Add to top - O(1)
push(element) {
this.items.push(element);
}
// Remove from top - O(1)
pop() {
if (this.isEmpty()) return null;
return this.items.pop();
}
// View top - O(1)
peek() {
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
}
A queue is a linear data structure that follows the First In, First Out (FIFO) principle. Elements are added at the rear and removed from the front. Like a line of people waiting for a service.
Add an element to the rear (end) of the queue. O(1) time complexity.
Remove an element from the front of the queue. O(1) time complexity.
View the front or rear element without removing. O(1) time complexity.
class Queue {
constructor() {
this.items = [];
this.front = 0;
this.rear = 0;
}
// Add to rear - O(1)
enqueue(element) {
this.items[this.rear] = element;
this.rear++;
}
// Remove from front - O(1)
dequeue() {
if (this.isEmpty()) return null;
const item = this.items[this.front];
this.front++;
return item;
}
// View front - O(1)
peek() {
return this.items[this.front];
}
isEmpty() {
return this.front === this.rear;
}
}
Understanding the key differences helps choose the right structure for your algorithm.
| Aspect | Stack | Queue |
|---|---|---|
| Principle | LIFO (Last In, First Out) | FIFO (First In, First Out) |
| Insertion | Push (Top) | Enqueue (Rear) |
| Deletion | Pop (Top) | Dequeue (Front) |
| Access Point | One end (Top) | Both ends (Front & Rear) |
| Time Complexity | O(1) for all ops | O(1) for all ops |
| Use Case | Undo, Recursion, Parsing | Scheduling, BFS, Buffering |
Choose the right implementation based on your constraints.
Simple and cache-friendly
Pros: Memory efficient, better cache locality, simple implementation
Cons: Fixed size (unless dynamic), shifting elements in queue is O(n)
// Stack with Array
class Stack {
constructor(capacity) {
this.items = new Array(capacity);
this.top = -1;
this.capacity = capacity;
}
push(item) {
if (this.top >= this.capacity - 1) {
throw new Error("Stack Overflow");
}
this.items[++this.top] = item;
}
pop() {
if (this.top < 0) return null;
return this.items[this.top--];
}
}
Dynamic size, flexible memory
Pros: Dynamic size, no memory waste, O(1) queue operations
Cons: Extra memory for pointers, poor cache locality
// Queue with Linked List
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}
class Queue {
constructor() {
this.front = null;
this.rear = null;
this.size = 0;
}
enqueue(data) {
const newNode = new Node(data);
if (!this.rear) {
this.front = this.rear = newNode;
} else {
this.rear.next = newNode;
this.rear = newNode;
}
this.size++;
}
}
Test your understanding with classic stack and queue problems.