Stacks and queues are fundamental linear data structures. This guide covers implementations, operations, and common problems.
Stack (LIFO - Last In First Out) #
Like a stack of plates - last one added is first one removed.
Stack Implementation #
class Stack {
constructor() {
this.items = [];
}
push(element) {
this.items.push(element);
}
pop() {
if (this.isEmpty()) return null;
return this.items.pop();
}
peek() {
if (this.isEmpty()) return null;
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
clear() {
this.items = [];
}
}
// Usage
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.pop()); // 3
console.log(stack.peek()); // 2
Stack Problems #
Valid Parentheses:
function isValid(s) {
const stack = [];
const pairs = { '(': ')', '[': ']', '{': '}' };
for (const char of s) {
if (char in pairs) {
stack.push(char);
} else {
const last = stack.pop();
if (pairs[last] !== char) return false;
}
}
return stack.length === 0;
}
console.log(isValid("()[]{}")); // true
console.log(isValid("([)]")); // false
Min Stack:
class MinStack {
constructor() {
this.stack = [];
this.minStack = [];
}
push(val) {
this.stack.push(val);
const min = this.minStack.length === 0
? val
: Math.min(val, this.minStack[this.minStack.length - 1]);
this.minStack.push(min);
}
pop() {
this.stack.pop();
this.minStack.pop();
}
top() {
return this.stack[this.stack.length - 1];
}
getMin() {
return this.minStack[this.minStack.length - 1];
}
}Evaluate Reverse Polish Notation:
function evalRPN(tokens) {
const stack = [];
for (const token of tokens) {
if (['+', '-', '*', '/'].includes(token)) {
const b = stack.pop();
const a = stack.pop();
if (token === '+') stack.push(a + b);
else if (token === '-') stack.push(a - b);
else if (token === '*') stack.push(a * b);
else stack.push(Math.trunc(a / b));
} else {
stack.push(Number(token));
}
}
return stack[0];
}
console.log(evalRPN(["2","1","+","3","*"])); // 9
Queue (FIFO - First In First Out) #
Like a line at a store - first person in line is served first.
Queue Implementation #
class Queue {
constructor() {
this.items = [];
}
enqueue(element) {
this.items.push(element);
}
dequeue() {
if (this.isEmpty()) return null;
return this.items.shift();
}
front() {
if (this.isEmpty()) return null;
return this.items[0];
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
clear() {
this.items = [];
}
}
// Usage
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
console.log(queue.dequeue()); // 1
console.log(queue.front()); // 2
Efficient Queue (Using Two Stacks) #
class QueueWithStacks {
constructor() {
this.inbox = [];
this.outbox = [];
}
enqueue(x) {
this.inbox.push(x);
}
dequeue() {
if (this.outbox.length === 0) {
while (this.inbox.length > 0) {
this.outbox.push(this.inbox.pop());
}
}
return this.outbox.pop();
}
peek() {
if (this.outbox.length === 0) {
while (this.inbox.length > 0) {
this.outbox.push(this.inbox.pop());
}
}
return this.outbox[this.outbox.length - 1];
}
empty() {
return this.inbox.length === 0 && this.outbox.length === 0;
}
}Circular Queue #
class CircularQueue {
constructor(k) {
this.queue = new Array(k);
this.head = -1;
this.tail = -1;
this.size = k;
}
enQueue(value) {
if (this.isFull()) return false;
if (this.isEmpty()) {
this.head = 0;
}
this.tail = (this.tail + 1) % this.size;
this.queue[this.tail] = value;
return true;
}
deQueue() {
if (this.isEmpty()) return false;
if (this.head === this.tail) {
this.head = -1;
this.tail = -1;
} else {
this.head = (this.head + 1) % this.size;
}
return true;
}
Front() {
return this.isEmpty() ? -1 : this.queue[this.head];
}
Rear() {
return this.isEmpty() ? -1 : this.queue[this.tail];
}
isEmpty() {
return this.head === -1;
}
isFull() {
return (this.tail + 1) % this.size === this.head;
}
}Deque (Double-Ended Queue) #
Can add/remove from both ends.
class Deque {
constructor() {
this.items = [];
}
addFront(element) {
this.items.unshift(element);
}
addRear(element) {
this.items.push(element);
}
removeFront() {
if (this.isEmpty()) return null;
return this.items.shift();
}
removeRear() {
if (this.isEmpty()) return null;
return this.items.pop();
}
peekFront() {
if (this.isEmpty()) return null;
return this.items[0];
}
peekRear() {
if (this.isEmpty()) return null;
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
}Common Problems #
Sliding Window Maximum #
function maxSlidingWindow(nums, k) {
const result = [];
const deque = []; // Store indices
for (let i = 0; i < nums.length; i++) {
// Remove elements outside window
while (deque.length > 0 && deque[0] <= i - k) {
deque.shift();
}
// Remove smaller elements
while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {
deque.pop();
}
deque.push(i);
// Add to result
if (i >= k - 1) {
result.push(nums[deque[0]]);
}
}
return result;
}
console.log(maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3));
// [3,3,5,5,6,7]
Daily Temperatures #
function dailyTemperatures(temperatures) {
const result = new Array(temperatures.length).fill(0);
const stack = [];
for (let i = 0; i < temperatures.length; i++) {
while (stack.length > 0 && temperatures[i] > temperatures[stack[stack.length - 1]]) {
const index = stack.pop();
result[index] = i - index;
}
stack.push(i);
}
return result;
}
console.log(dailyTemperatures([73,74,75,71,69,72,76,73]));
// [1,1,4,2,1,1,0,0]
Implement Stack Using Queues #
class MyStack {
constructor() {
this.queue = [];
}
push(x) {
this.queue.push(x);
for (let i = 0; i < this.queue.length - 1; i++) {
this.queue.push(this.queue.shift());
}
}
pop() {
return this.queue.shift();
}
top() {
return this.queue[0];
}
empty() {
return this.queue.length === 0;
}
}Binary Tree Level Order Traversal #
function levelOrder(root) {
if (!root) return [];
const result = [];
const queue = [root];
while (queue.length > 0) {
const levelSize = queue.length;
const currentLevel = [];
for (let i = 0; i < levelSize; i++) {
const node = queue.shift();
currentLevel.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(currentLevel);
}
return result;
}Next Greater Element #
function nextGreaterElement(nums) {
const result = new Array(nums.length).fill(-1);
const stack = [];
for (let i = 0; i < nums.length; i++) {
while (stack.length > 0 && nums[i] > nums[stack[stack.length - 1]]) {
const index = stack.pop();
result[index] = nums[i];
}
stack.push(i);
}
return result;
}
console.log(nextGreaterElement([2,1,2,4,3]));
// [4,2,4,-1,-1]
Simplify Path #
function simplifyPath(path) {
const stack = [];
const parts = path.split('/');
for (const part of parts) {
if (part === '' || part === '.') {
continue;
} else if (part === '..') {
stack.pop();
} else {
stack.push(part);
}
}
return '/' + stack.join('/');
}
console.log(simplifyPath("/a/./b/../../c/")); // "/c"
Decode String #
function decodeString(s) {
const numStack = [];
const strStack = [];
let currentNum = 0;
let currentStr = '';
for (const char of s) {
if (char >= '0' && char <= '9') {
currentNum = currentNum * 10 + Number(char);
} else if (char === '[') {
numStack.push(currentNum);
strStack.push(currentStr);
currentNum = 0;
currentStr = '';
} else if (char === ']') {
const repeatTimes = numStack.pop();
const prevStr = strStack.pop();
currentStr = prevStr + currentStr.repeat(repeatTimes);
} else {
currentStr += char;
}
}
return currentStr;
}
console.log(decodeString("3[a2[c]]")); // "accaccacc"
Time Complexity #
| Operation | Stack | Queue |
|---|---|---|
| Push/Enqueue | O(1) | O(1) |
| Pop/Dequeue | O(1) | O(1) |
| Peek/Front | O(1) | O(1) |
| Search | O(n) | O(n) |
| Space | O(n) | O(n) |
When to Use #
Use Stack when:
- Need LIFO behavior
- Reversing order
- Parsing expressions
- Backtracking
- Undo operations
Use Queue when:
- Need FIFO behavior
- BFS traversal
- Task scheduling
- Buffer management
- Event handling
Stacks and queues are essential building blocks. Master these structures and their applications to solve many algorithmic problems.