Stacks & Queues

36 problems · 36 approaches

Next Greater ElementMedium1 approach

Problem

For each element, find the first element to its right that is strictly greater. Use −1 when there is none.

Example 1

Input:  [4, 5, 2, 25]
Output: [5, 25, 25, -1]

Example 2

Input:  [13, 7, 6, 12]
Output: [-1, 12, 12, -1]

Also asked as: Find the next Greater element

1. Monotonic decreasing stack

Time O(n)Space O(n)

Push indices; when the current value beats the stack top, it is that index’s next-greater. Each index is pushed/popped once.

function nextGreater(nums) {
const res = Array(nums.length).fill(-1);
const st = [];
for (let i = 0; i < nums.length; i++) {
while (st.length && nums[i] > nums[st[st.length - 1]]) res[st.pop()] = nums[i];
st.push(i);
}
return res;
}
Implement Queue using StackEasy1 approach

Problem

Implement a FIFO queue with push, pop, peek and empty, using only two stacks. Every operation should be O(1) amortised.

Example 1

Input:  push(1), push(2), peek(), pop(), empty()
Output: 1, 1, false

Also asked as: implement queue using stacks

1. Two stacks, lazy transfer

Time O(1) amortisedSpace O(n)

Push onto `inbox`. To dequeue, if `outbox` is empty pour `inbox` into it (reversing order), then pop. Amortised O(1).

class MyQueue {
constructor() { this.inbox = []; this.outbox = []; }
push(x) { this.inbox.push(x); }
pop() { this.peek(); return this.outbox.pop(); }
peek() {
if (!this.outbox.length) while (this.inbox.length) this.outbox.push(this.inbox.pop());
return this.outbox[this.outbox.length - 1];
}
empty() { return !this.inbox.length && !this.outbox.length; }
}
Design a stack that supports getMin in O(1)Easy1 approach

Problem

Design a stack that supports push, pop, top and getMin (return the current minimum), each in O(1) time. The hard version allows only O(1) extra space.

Example 1

Input:  push(-2), push(0), push(-3), getMin(), pop(), top(), getMin()
Output: -3, 0, -2

Also asked as: min stack · Design a Stack that supports getMin() in O(1) time and O(1) extra space

1. Parallel min-stack

Time O(1) per opSpace O(n)

Each entry also stores the minimum of the stack up to that point.

class MinStack {
constructor() { this.s = []; }
push(x) { this.s.push([x, this.s.length ? Math.min(x, this.getMin()) : x]); }
pop() { this.s.pop(); }
top() { return this.s[this.s.length - 1][0]; }
getMin() { return this.s[this.s.length - 1][1]; }
}
Implement a stack from scratchEasy1 approach

Problem

Implement a stack (LIFO) with push, pop, peek, isEmpty and size, backed by an array or a linked list. Handle popping from an empty stack.

Example 1

Input:  push(1), push(2), pop(), peek()
Output: 2, 1

Also asked as: Implement Stack from Scratch

1. Backed by an array

Time O(1) per opSpace O(n)

push/pop/peek at the array end are O(1); track size explicitly if you avoid `length`.

class Stack {
#a = [];
push(x) { this.#a.push(x); }
pop() { return this.#a.pop(); }
peek() { return this.#a[this.#a.length - 1]; }
isEmpty() { return this.#a.length === 0; }
size() { return this.#a.length; }
}
Implement a queue from scratchEasy1 approach

Problem

Implement a queue (FIFO) with enqueue, dequeue, front, isEmpty and size, where every operation is O(1). A plain array with shift() is O(n) per dequeue.

Example 1

Input:  enqueue(1), enqueue(2), dequeue(), front()
Output: 1, 2

Also asked as: Implement Queue from Scratch

1. Circular buffer

Time O(1) per opSpace O(capacity)

Fixed array with head/tail indices modulo capacity; O(1) enqueue/dequeue with no shifting.

class Queue {
constructor(cap = 1024) { this.a = new Array(cap); this.head = 0; this.tail = 0; this.count = 0; this.cap = cap; }
enqueue(x) { if (this.count === this.cap) throw new Error('full'); this.a[this.tail] = x; this.tail = (this.tail + 1) % this.cap; this.count++; }
dequeue() { if (!this.count) return undefined; const x = this.a[this.head]; this.head = (this.head + 1) % this.cap; this.count--; return x; }
front() { return this.count ? this.a[this.head] : undefined; }
isEmpty() { return this.count === 0; }
}
Implement two stacks in one arrayEasy1 approach

Problem

Implement two independent stacks inside a single fixed-size array so that neither overflows while free space remains anywhere in the array.

Example 1

Input:  size 5: push1(1), push2(5), push2(4), pop1(), pop2()
Output: 1, 4

Also asked as: Implement 2 stack in an array · Implement "N" stacks in an Array · Implement "n" queue in an array

1. Grow from both ends

Time O(1) per opSpace O(n)

Stack 1 grows from index 0 upward, stack 2 grows from the last index downward; they collide only when the array is full.

class TwoStacks {
constructor(n) { this.a = new Array(n); this.top1 = -1; this.top2 = n; }
push1(x) { if (this.top1 + 1 === this.top2) throw new Error('full'); this.a[++this.top1] = x; }
push2(x) { if (this.top2 - 1 === this.top1) throw new Error('full'); this.a[--this.top2] = x; }
pop1() { return this.top1 >= 0 ? this.a[this.top1--] : undefined; }
pop2() { return this.top2 < this.a.length ? this.a[this.top2++] : undefined; }
}

For N stacks in one array: keep a `next[]` free-list and a `top[]` per stack; each cell stores the index of the previous element of its stack.

Find the middle element of a stack in O(1)Medium1 approach

Problem

Design a stack that supports push, pop, findMiddle and deleteMiddle, each in O(1) time.

Example 1

Input:  push 1, 2, 3, 4, 5 → findMiddle()
Output: 3

Also asked as: find the middle element of a stack

1. Doubly linked list + middle pointer

Time O(1) push / pop / findMiddleSpace O(n)

Back the stack with a DLL. Keep a pointer to the middle node and a count; on push/pop, move the middle pointer at most one step.

class MiddleStack {
constructor() { this.head = null; this.mid = null; this.count = 0; }
push(x) {
const node = { val: x, prev: null, next: this.head };
if (this.head) this.head.prev = node;
this.head = node;
this.count++;
if (this.count === 1) this.mid = node;
else if (this.count % 2 === 1) this.mid = this.mid.prev;
}
pop() {
if (!this.count) return undefined;
const x = this.head.val;
this.head = this.head.next;
if (this.head) this.head.prev = null;
this.count--;
if (this.count % 2 === 0) this.mid = this.mid ? this.mid.next : null;
return x;
}
findMiddle() { return this.mid ? this.mid.val : undefined; }
}
The celebrity problemMedium1 approach

Problem

At a party of n people, a celebrity is someone everyone knows but who knows nobody. M[i][j] = 1 means person i knows person j. Return the celebrity's index, or −1 if there is none. Aim for O(n).

Example 1

Input:  M = [[0,1,0], [0,0,0], [0,1,0]]
Output: 1

Everyone knows person 1, and person 1 knows nobody.

Also asked as: The celebrity Problem

1. Elimination with a stack / two pointers

Time O(n)Space O(1)

Push everyone; repeatedly pop two — if a knows b, a is not the celebrity (keep b), else keep a. One candidate remains; verify they know nobody and everybody knows them.

function findCelebrity(n, knows) {
let cand = 0;
for (let i = 1; i < n; i++) if (knows(cand, i)) cand = i;
for (let i = 0; i < n; i++) {
if (i === cand) continue;
if (knows(cand, i) || !knows(i, cand)) return -1;
}
return cand;
}
Evaluate a postfix expressionEasy1 approach

Problem

Evaluate an expression in postfix (Reverse Polish) notation, where every operator comes after its two operands. Integer division truncates toward zero.

Example 1

Input:  ["2", "1", "+", "3", "*"]
Output: 9

(2 + 1) × 3.

Example 2

Input:  ["4", "13", "5", "/", "+"]
Output: 6

4 + (13 / 5) = 4 + 2.

Also asked as: Evaluation of Postfix expression

1. Operand stack

Time O(n)Space O(n)

Push numbers; on an operator, pop two, apply, push the result.

function evalPostfix(tokens) {
const st = [];
const ops = { '+': (a, b) => a + b, '-': (a, b) => a - b, '*': (a, b) => a * b, '/': (a, b) => Math.trunc(a / b) };
for (const t of tokens) {
if (t in ops) { const b = st.pop(), a = st.pop(); st.push(ops[t](a, b)); }
else st.push(Number(t));
}
return st.pop();
}
Evaluate an infix arithmetic expressionMedium1 approach

Problem

Evaluate a normal (infix) expression string with non-negative integers, + − * /, parentheses and spaces, respecting operator precedence. Integer division truncates.

Example 1

Input:  "3 + 2 * 2"
Output: 7

Example 2

Input:  "(1 + (4 + 5 + 2) - 3) + (6 + 8)"
Output: 23

Also asked as: Arithmetic Expression evaluation

1. Two stacks (values + operators) with precedence

Time O(n)Space O(n)

Scan tokens; push numbers; on an operator, first resolve any stacked operator of higher/equal precedence; handle parentheses by resolving until "(".

function evalInfix(expr) {
const nums = [], ops = [];
const prec = (o) => (o === '+' || o === '-' ? 1 : o === '*' || o === '/' ? 2 : 0);
const apply = () => {
const b = nums.pop(), a = nums.pop(), o = ops.pop();
nums.push(o === '+' ? a + b : o === '-' ? a - b : o === '*' ? a * b : Math.trunc(a / b));
};
const tokens = expr.match(/\d+|[()+\-*/]/g) || [];
for (const t of tokens) {
if (/\d/.test(t)) nums.push(Number(t));
else if (t === '(') ops.push(t);
else if (t === ')') { while (ops[ops.length - 1] !== '(') apply(); ops.pop(); }
else { while (ops.length && prec(ops[ops.length - 1]) >= prec(t)) apply(); ops.push(t); }
}
while (ops.length) apply();
return nums[0];
}
Insert an element at the bottom of a stack using recursionEasy1 approach

Problem

Push x to the bottom of a stack using only push, pop and recursion — no other data structure.

Example 1

Input:  stack (bottom → top) [1, 2, 3], x = 0
Output: [0, 1, 2, 3]

Also asked as: Implement a method to insert an element at its bottom without using any other data structure

1. Recursively pop to the bottom, place, unwind

Time O(n)Space O(n) call stack

If the stack is empty, push x. Otherwise pop the top, recurse, then push the top back.

function insertAtBottom(stack, x) {
if (stack.length === 0) { stack.push(x); return; }
const top = stack.pop();
insertAtBottom(stack, x);
stack.push(top);
}
Reverse a stack using recursionEasy1 approach

Problem

Reverse a stack using only its push and pop operations and recursion — no extra array or stack.

Example 1

Input:  (bottom → top) [1, 2, 3, 4]
Output: [4, 3, 2, 1]

Also asked as: Reverse a stack using recursion

1. Pop all, then insert each at the bottom

Time O(n²)Space O(n)

Recursively empty the stack; as the recursion unwinds, insert each popped value at the bottom.

function reverseStack(stack) {
if (stack.length === 0) return;
const top = stack.pop();
reverseStack(stack);
insertAtBottom(stack, top);
}
function insertAtBottom(stack, x) {
if (!stack.length) { stack.push(x); return; }
const t = stack.pop();
insertAtBottom(stack, x);
stack.push(t);
}
Sort a stack using recursionMedium1 approach

Problem

Sort a stack so the largest element is on top, using only push, pop, peek and recursion.

Example 1

Input:  (bottom → top) [34, 3, 31, 98, 92, 23]
Output: [3, 23, 31, 34, 92, 98]

Also asked as: Sort a Stack using recursion

1. Recursively sort, then insert in order

Time O(n²)Space O(n)

Pop the top, sort the rest, then insert the top back into its sorted position (another recursive helper).

function sortStack(s) {
if (s.length === 0) return;
const top = s.pop();
sortStack(s);
sortedInsert(s, top);
}
function sortedInsert(s, x) {
if (!s.length || s[s.length - 1] <= x) { s.push(x); return; }
const t = s.pop();
sortedInsert(s, x);
s.push(t);
}
Largest rectangular area in a histogramHard1 approach

Problem

heights[i] is the height of a bar of width 1. Return the area of the largest rectangle that fits entirely inside the histogram.

Example 1

Input:  [2, 1, 5, 6, 2, 3]
Output: 10

The bars of height 5 and 6 give a 5 × 2 rectangle.

Also asked as: Largest rectangular Area in Histogram

1. Monotonic increasing stack of indices

Time O(n)Space O(n)

When a shorter bar appears, pop taller bars; each popped bar’s max rectangle uses it as the height, bounded by the new bar and the stack’s new top.

function largestRectangleArea(h) {
const st = [];
let best = 0;
for (let i = 0; i <= h.length; i++) {
const cur = i === h.length ? 0 : h[i];
while (st.length && h[st[st.length - 1]] >= cur) {
const height = h[st.pop()];
const width = st.length ? i - st[st.length - 1] - 1 : i;
best = Math.max(best, height * width);
}
st.push(i);
}
return best;
}
Length of the longest valid parentheses substringHard1 approach

Problem

Given a string of "(" and ")", return the length of its longest substring that is well-formed (balanced).

Example 1

Input:  "(()"
Output: 2

Example 2

Input:  ")()())"
Output: 4

"()()".

Also asked as: Length of the Longest Valid Substring

1. Stack of indices with a base marker

Time O(n)Space O(n)

Push −1 as a base. On "(", push its index. On ")", pop; if the stack is now empty push this index as the new base, else the current valid length is i − stack top.

function longestValidParentheses(s) {
const st = [-1];
let best = 0;
for (let i = 0; i < s.length; i++) {
if (s[i] === '(') st.push(i);
else {
st.pop();
if (st.length === 0) st.push(i);
else best = Math.max(best, i - st[st.length - 1]);
}
}
return best;
}
Check if an expression has redundant bracketsMedium1 approach

Problem

Return true if the expression contains a pair of parentheses with no operator directly inside it, such as "((a+b))" or "(a)".

Example 1

Input:  "((a+b))"
Output: true

Example 2

Input:  "(a+(b)/c)"
Output: true

"(b)" is redundant.

Example 3

Input:  "(a+b*(c-d))"
Output: false

Also asked as: Expression contains redundant bracket or not

1. Stack — a ")" with no operator since its "(" is redundant

Time O(n)Space O(n)

Push everything except ")". On ")", pop until "("; if you saw no operator in between, the brackets were redundant.

function hasRedundantBrackets(expr) {
const st = [];
for (const c of expr) {
if (c === ')') {
let hasOp = false;
while (st.length && st[st.length - 1] !== '(') {
if ('+-*/'.includes(st.pop())) hasOp = true;
}
st.pop(); // '('
if (!hasOp) return true;
} else st.push(c);
}
return false;
}
Implement a stack using queuesEasy1 approach

Problem

Implement a LIFO stack with push, pop, top and empty using only queue operations (enqueue to the back, dequeue from the front, size).

Example 1

Input:  push(1), push(2), top(), pop(), empty()
Output: 2, 2, false

Also asked as: Implement Stack using Queue · Implement Stack using Deque

1. Single queue, rotate on push

Time push O(n), pop O(1)Space O(n)

After enqueueing x, rotate the queue so x is at the front (dequeue and re-enqueue the previous elements). push O(n), pop/top O(1).

class StackViaQueue {
#q = [];
push(x) { this.#q.push(x); for (let i = 0; i < this.#q.length - 1; i++) this.#q.push(this.#q.shift()); }
pop() { return this.#q.shift(); }
top() { return this.#q[0]; }
empty() { return this.#q.length === 0; }
}
Check if an array is a valid stack permutation of anotherMedium1 approach

Problem

Elements of the input array are pushed onto a stack in order, and you may pop at any time. Return true if the output array can be produced as the sequence of popped values.

Example 1

Input:  input = [1, 2, 3], output = [2, 1, 3]
Output: true

Example 2

Input:  input = [1, 2, 3], output = [3, 1, 2]
Output: false

Also asked as: Stack Permutations (Check if an array is stack permutation of other)

1. Simulate with an auxiliary stack

Time O(n)Space O(n)

Push input elements one by one; whenever the stack top equals the next expected output element, pop. If the stack empties out matching the whole output, it is a valid permutation.

function isStackPermutation(input, output) {
const st = [];
let j = 0;
for (const x of input) {
st.push(x);
while (st.length && st[st.length - 1] === output[j]) { st.pop(); j++; }
}
return st.length === 0 && j === output.length;
}
Implement a circular queueEasy1 approach

Problem

Implement a fixed-capacity circular queue (ring buffer) with enQueue, deQueue, Front, Rear, isEmpty and isFull. The front and rear indices wrap around the array.

Example 1

Input:  k = 3: enQueue 1, 2, 3, 4 → Rear() → isFull() → deQueue() → enQueue(4) → Rear()
Output: true, true, true, false, 3, true, true, true, 4

Also asked as: Implement a Circular queue

1. Fixed array with modular head/tail

Time O(1) per opSpace O(capacity)

Wrap indices with `% capacity`; track a count to distinguish full from empty.

class CircularQueue {
constructor(k) { this.a = new Array(k); this.k = k; this.head = 0; this.count = 0; }
enqueue(x) { if (this.count === this.k) return false; this.a[(this.head + this.count) % this.k] = x; this.count++; return true; }
dequeue() { if (!this.count) return false; this.head = (this.head + 1) % this.k; this.count--; return true; }
front() { return this.count ? this.a[this.head] : -1; }
rear() { return this.count ? this.a[(this.head + this.count - 1) % this.k] : -1; }
isFull() { return this.count === this.k; }
isEmpty() { return this.count === 0; }
}
LRU CacheMedium1 approach

Problem

Design a Least Recently Used cache with a fixed capacity. get(key) returns the value, or −1 if absent, and marks the key as recently used. put(key, value) inserts or updates the key; when the cache is full, it first evicts the least recently used key. Both operations must be O(1).

Example 1

Input:  capacity 2: put(1,1), put(2,2), get(1), put(3,3), get(2), put(4,4), get(1), get(3), get(4)
Output: 1, -1, -1, 3, 4

Also asked as: LRU Cache Implementationa · Program for Least Recently Used (LRU) Page Replacement algorithm

1. Hash map + JS Map insertion order

Time O(1) per opSpace O(capacity)

A Map keeps insertion order. On get, delete and re-set the key to mark it most-recent. On put past capacity, delete the first key (least-recent).

class LRUCache {
constructor(capacity) { this.cap = capacity; this.map = new Map(); }
get(key) {
if (!this.map.has(key)) return -1;
const v = this.map.get(key);
this.map.delete(key);
this.map.set(key, v);
return v;
}
put(key, value) {
if (this.map.has(key)) this.map.delete(key);
else if (this.map.size === this.cap) this.map.delete(this.map.keys().next().value);
this.map.set(key, value);
}
}

The "proper" version uses a hash map to nodes of a doubly linked list; same O(1), no reliance on Map ordering.

Reverse the first K elements of a queueEasy1 approach

Problem

Reverse the order of the first k elements of a queue and leave the rest in their original order.

Example 1

Input:  [1, 2, 3, 4, 5], k = 3
Output: [3, 2, 1, 4, 5]

Also asked as: Reverse the first “K” elements of a queue

1. Stack for the first K, then rotate the rest

Time O(n)Space O(k)

Dequeue K into a stack, enqueue them back (reversed), then move the remaining n−K elements from front to back.

function reverseFirstK(queue, k) {
const st = [];
for (let i = 0; i < k; i++) st.push(queue.shift());
while (st.length) queue.push(st.pop());
for (let i = 0; i < queue.length - k; i++) queue.push(queue.shift());
return queue;
}
Interleave the first half of a queue with the second halfMedium1 approach

Problem

Given a queue of even length, interleave its first half with its second half: first[0], second[0], first[1], second[1], …

Example 1

Input:  [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Output: [11, 16, 12, 17, 13, 18, 14, 19, 15, 20]

Also asked as: Interleave the first half of the queue with second half

1. Move first half to a stack, re-queue, then interleave

Time O(n)Space O(n)

Push the first n/2 to a stack, enqueue back (now the front half is reversed and rotated), rotate n/2, then alternately pull from stack-equivalent and queue.

function interleaveQueue(q) {
const half = q.length / 2;
const st = [];
for (let i = 0; i < half; i++) st.push(q.shift());
while (st.length) q.push(st.pop());
for (let i = 0; i < half; i++) q.push(q.shift());
for (let i = 0; i < half; i++) { st.push(q.shift()); }
const res = [];
for (let i = 0; i < half; i++) { res.push(st.shift()); res.push(q.shift()); }
q.push(...res);
return q;
}
First circular tour that visits all petrol pumpsMedium1 approach

Problem

Petrol pumps sit on a circular road. Pump i gives petrol[i] litres, and reaching the next pump costs distance[i] litres. Starting with an empty tank, return the first pump index from which you can complete the whole circle, or −1 if none works.

Example 1

Input:  petrol = [4, 6, 7, 4], distance = [6, 5, 3, 5]
Output: 1

Also asked as: Find the first circular tour that visits all Petrol Pumps

1. Single pass — reset start on deficit

Time O(n)Space O(1)

Track a running tank. If it ever goes negative, no start up to here works; set start to the next pump and reset the tank. Feasible overall iff total petrol ≥ total distance.

function firstTour(pumps) { // pumps[i] = [petrol, distance]
let start = 0, tank = 0, total = 0;
for (let i = 0; i < pumps.length; i++) {
const diff = pumps[i][0] - pumps[i][1];
tank += diff;
total += diff;
if (tank < 0) { start = i + 1; tank = 0; }
}
return total >= 0 ? start : -1;
}
Rotten oranges (minimum time to rot all)Medium1 approach

Problem

In the grid, 0 is empty, 1 is a fresh orange and 2 is a rotten orange. Every minute, a fresh orange next to a rotten one (up, down, left or right) becomes rotten. Return the minimum number of minutes until no fresh orange remains, or −1 if that never happens.

Example 1

Input:  [[2,1,1], [1,1,0], [0,1,1]]
Output: 4

Example 2

Input:  [[2,1,1], [0,1,1], [1,0,1]]
Output: -1

The bottom-left orange can never be reached.

Also asked as: Minimum time required to rot all oranges

1. Multi-source BFS

Time O(R·C)Space O(R·C)

Seed the queue with every rotten orange at time 0; BFS outward, rotting fresh neighbours. The answer is the last time stamp; return −1 if any fresh orange remains.

function orangesRotting(grid) {
const R = grid.length, C = grid[0].length;
let fresh = 0;
const q = [];
for (let r = 0; r < R; r++)
for (let c = 0; c < C; c++) {
if (grid[r][c] === 2) q.push([r, c, 0]);
else if (grid[r][c] === 1) fresh++;
}
let time = 0;
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
while (q.length) {
const [r, c, t] = q.shift();
time = Math.max(time, t);
for (const [dr, dc] of dirs) {
const nr = r + dr, nc = c + dc;
if (nr >= 0 && nc >= 0 && nr < R && nc < C && grid[nr][nc] === 1) {
grid[nr][nc] = 2; fresh--; q.push([nr, nc, t + 1]);
}
}
}
return fresh === 0 ? time : -1;
}
Distance of the nearest 1 in a binary matrixMedium1 approach

Problem

For every cell of a binary matrix, return the distance (in up/down/left/right steps) to the nearest cell containing 1. The LeetCode variant, 01 Matrix, asks for the nearest 0 instead — same method.

Example 1

Input:  [[0,1,1,0], [1,1,0,0], [0,0,1,1]]
Output: [[1,0,0,1], [0,0,1,1], [1,1,0,0]]

Also asked as: Distance of nearest cell having 1 in a binary matrix

1. Multi-source BFS from all 1s

Time O(R·C)Space O(R·C)

Push every cell containing 1 with distance 0; BFS outward filling each 0 with its shortest distance.

function nearestOne(grid) {
const R = grid.length, C = grid[0].length;
const dist = Array.from({ length: R }, () => Array(C).fill(-1));
const q = [];
for (let r = 0; r < R; r++)
for (let c = 0; c < C; c++)
if (grid[r][c] === 1) { dist[r][c] = 0; q.push([r, c]); }
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
let head = 0;
while (head < q.length) {
const [r, c] = q[head++];
for (const [dr, dc] of dirs) {
const nr = r + dr, nc = c + dc;
if (nr >= 0 && nc >= 0 && nr < R && nc < C && dist[nr][nc] === -1) {
dist[nr][nc] = dist[r][c] + 1;
q.push([nr, nc]);
}
}
}
return dist;
}
First negative integer in every window of size kMedium1 approach

Problem

For every contiguous window of size k, report its first negative number, or 0 if the window has none.

Example 1

Input:  arr = [-8, 2, 3, -6, 10], k = 2
Output: [-8, 0, -6, -6]

Also asked as: First negative integer in every window of size “k”

1. Deque of indices of negatives

Time O(n)Space O(k)

Maintain a queue of indices of negative numbers in the current window; the front is the answer, and it is popped when it slides out.

function firstNegativeEachWindow(a, k) {
const dq = [], res = [];
for (let i = 0; i < a.length; i++) {
if (a[i] < 0) dq.push(i);
if (i >= k - 1) {
while (dq.length && dq[0] <= i - k) dq.shift();
res.push(dq.length ? a[dq[0]] : 0);
}
}
return res;
}
Sum of minimum and maximum of all subarrays of size kMedium1 approach

Problem

For every contiguous window of size k, add its minimum and its maximum. Return the total over all windows.

Example 1

Input:  arr = [2, 5, -1, 7, -3, -1, -2], k = 4
Output: 18

Windows give (−1+7) + (−3+7) + (−3+7) + (−3+7) = 18.

Also asked as: Sum of minimum and maximum elements of all subarrays of size “k”

1. Two monotonic deques (max and min)

Time O(n)Space O(k)

A decreasing deque gives the window max at its front; an increasing deque gives the window min. Slide and add both.

function sumOfMinMax(a, k) {
const maxDq = [], minDq = [];
let sum = 0;
for (let i = 0; i < a.length; i++) {
while (maxDq.length && a[maxDq[maxDq.length - 1]] <= a[i]) maxDq.pop();
while (minDq.length && a[minDq[minDq.length - 1]] >= a[i]) minDq.pop();
maxDq.push(i); minDq.push(i);
if (maxDq[0] <= i - k) maxDq.shift();
if (minDq[0] <= i - k) minDq.shift();
if (i >= k - 1) sum += a[maxDq[0]] + a[minDq[0]];
}
return sum;
}
Minimum sum of squares of character counts after removing k charactersMedium1 approach

Problem

Remove exactly k characters from the string so that the sum of the squares of the remaining character counts is as small as possible. Return that sum.

Example 1

Input:  s = "abccc", k = 1
Output: 6

Remove one c: counts 1, 1, 2 → 1 + 1 + 4.

Also asked as: Minimum sum of squares of character counts in a given string after removing “k” characters

1. Greedy — always decrement the current max frequency

Time O(k log 26 + n)Space O(1)

Removing a character helps most when taken from the most frequent one (n² − (n−1)² = 2n−1 is largest for large n). Use a max-heap of frequencies, k times.

function minStringValue(s, k) {
const freq = new Array(26).fill(0);
for (const c of s) freq[c.charCodeAt(0) - 97]++;
for (let i = 0; i < k; i++) {
let mx = 0;
for (let j = 1; j < 26; j++) if (freq[j] > freq[mx]) mx = j;
if (freq[mx] === 0) break;
freq[mx]--;
}
return freq.reduce((acc, f) => acc + f * f, 0);
}
Next Smaller ElementMedium1 approach

Problem

For each element, find the first element to its right that is strictly smaller. Use −1 when there is none.

Example 1

Input:  [4, 8, 5, 2, 25]
Output: [2, 5, 2, -1, -1]

Also asked as: Next Smaller Element

1. Monotonic increasing stack

Time O(n)Space O(n)

Scan; pop while the stack top is greater than the current value — the current value is those elements’ next smaller.

function nextSmaller(a) {
const res = Array(a.length).fill(-1);
const st = [];
for (let i = 0; i < a.length; i++) {
while (st.length && a[st[st.length - 1]] > a[i]) res[st.pop()] = a[i];
st.push(i);
}
return res;
}
Reverse a string / queue using a stack; check balanced parenthesesEasy1 approach

Problem

Three warm-ups that show how a stack works: reverse a string with a stack; reverse a queue with a stack; check that a string of brackets is balanced.

Example 1

Input:  reverse "stack"
Output: "kcats"

Example 2

Input:  balanced? "[()]{}"
Output: true

Also asked as: Reverse a String using Stack · Reverse a Queue using recursion · Check the expression has valid or Balanced parenthesis or not

1. Stack fundamentals

Time O(n)Space O(n)

Push all characters/elements and pop them to reverse. For balanced parentheses, push openers and match each closer against the stack top. To reverse a queue recursively: dequeue the front, recurse, then enqueue the saved front at the back.

const reverseWithStack = (s) => { const st = [...s]; let r = ''; while (st.length) r += st.pop(); return r; };
function isBalanced(s) {
const pair = { ')': '(', ']': '[', '}': '{' }, st = [];
for (const c of s) {
if ('([{'.includes(c)) st.push(c);
else if (st.pop() !== pair[c]) return false;
}
return st.length === 0;
}
function reverseQueue(q) {
if (q.length === 0) return;
const front = q.shift();
reverseQueue(q);
q.push(front);
}
Daily TemperaturesMedium1 approach

Problem

For each day, return how many days you must wait until a warmer temperature, or 0 if no warmer day follows.

Example 1

Input:  [73, 74, 75, 71, 69, 72, 76, 73]
Output: [1, 1, 4, 2, 1, 1, 0, 0]

1. Monotonic decreasing stack of indices

Time O(n)Space O(n)

Keep days still waiting for a warmer one. A warmer day pops and answers every colder day on top.

function dailyTemperatures(t) {
const res = new Array(t.length).fill(0), st = [];
for (let i = 0; i < t.length; i++) {
while (st.length && t[i] > t[st[st.length - 1]]) {
const j = st.pop();
res[j] = i - j;
}
st.push(i);
}
return res;
}
Next Greater Element IIMedium1 approach

Problem

The array is circular: the element after the last one is the first. For each element, return the first greater element found by moving forward (wrapping around), or −1 if there is none.

Example 1

Input:  [1, 2, 1]
Output: [2, -1, 2]

The last 1 wraps around to find 2.

1. Monotonic stack over two passes

Time O(n)Space O(n)

Circular array: iterate 2n times using i % n so elements near the end can see the start. Only push during the first pass.

function nextGreaterElements(nums) {
const n = nums.length, res = new Array(n).fill(-1), st = [];
for (let i = 0; i < 2 * n; i++) {
const x = nums[i % n];
while (st.length && x > nums[st[st.length - 1]]) res[st.pop()] = x;
if (i < n) st.push(i);
}
return res;
}
Decode StringMedium1 approach

Problem

Decode a string encoded as k[encoded], meaning the part inside the brackets repeated k times. Brackets can be nested, and the input is always valid.

Example 1

Input:  "3[a]2[bc]"
Output: "aaabcbc"

Example 2

Input:  "3[a2[c]]"
Output: "accaccacc"

1. Stack of (previous string, repeat count)

Time O(output length)Space O(output length)

On "[" save the current string and number and start fresh; on "]" pop and append the current string repeated.

function decodeString(s) {
const st = [];
let cur = '', num = 0;
for (const c of s) {
if (c >= '0' && c <= '9') num = num * 10 + Number(c);
else if (c === '[') { st.push([cur, num]); cur = ''; num = 0; }
else if (c === ']') { const [prev, k] = st.pop(); cur = prev + cur.repeat(k); }
else cur += c;
}
return cur;
}
Asteroid CollisionMedium1 approach

Problem

Asteroids move along a line: the absolute value is the size and the sign is the direction (+ right, − left). When two meet, the smaller one explodes, and if they are the same size both explode. Asteroids moving the same way never meet. Return the state after all collisions.

Example 1

Input:  [5, 10, -5]
Output: [5, 10]

Example 2

Input:  [8, -8]
Output: []

Example 3

Input:  [10, 2, -5]
Output: [10]

1. Stack of survivors

Time O(n)Space O(n)

Only a left-moving asteroid meeting a right-moving one on the stack collides. Pop smaller ones; destroy both on a tie.

function asteroidCollision(asteroids) {
const st = [];
for (const a of asteroids) {
let alive = true;
while (alive && a < 0 && st.length && st[st.length - 1] > 0) {
const top = st[st.length - 1];
if (top < -a) st.pop();
else { if (top === -a) st.pop(); alive = false; }
}
if (alive) st.push(a);
}
return st;
}
Online Stock SpanMedium1 approach

Problem

Prices arrive one per day. For each new price, return its span: the number of consecutive days, ending today and going backwards, on which the price was ≤ today's price.

Example 1

Input:  100, 80, 60, 70, 60, 75, 85
Output: 1, 1, 1, 2, 1, 4, 6

1. Monotonic stack of [price, span]

Time O(1) amortised per callSpace O(n)

Absorb every earlier price ≤ today’s, adding their spans. Each price is pushed and popped once.

class StockSpanner {
constructor() { this.st = []; }
next(price) {
let span = 1;
while (this.st.length && this.st[this.st.length - 1][0] <= price) span += this.st.pop()[1];
this.st.push([price, span]);
return span;
}
}
Shortest Subarray with Sum at Least KHard1 approach

Problem

Return the length of the shortest non-empty contiguous subarray whose sum is at least k, or −1 if none exists. The array can contain negative numbers.

Example 1

Input:  nums = [2, -1, 2], k = 3
Output: 3

Example 2

Input:  nums = [1, 2], k = 4
Output: -1

1. Prefix sums + monotonic deque

Time O(n)Space O(n)

Keep candidate start prefixes in increasing order. While the current prefix minus the front is ≥ k, record and drop the front (it cannot do better later). Drop back entries ≥ current prefix — a later, smaller start is always better. Negatives are why a plain sliding window fails.

function shortestSubarray(nums, k) {
const n = nums.length, pre = [0];
for (const x of nums) pre.push(pre[pre.length - 1] + x);
const dq = []; let head = 0, best = Infinity;
for (let i = 0; i <= n; i++) {
while (head < dq.length && pre[i] - pre[dq[head]] >= k) best = Math.min(best, i - dq[head++]);
while (dq.length > head && pre[dq[dq.length - 1]] >= pre[i]) dq.pop();
dq.push(i);
}
return best === Infinity ? -1 : best;
}