Skip to the notes
JSGroundwork
JSGroundwork handwritten · web dev
✎Playground→⌘Problems↻Review🔥Progress

Chapters

34 chapters
⌕
Beginner10›
B1Complexity analysisB2Arrays & stringsB3HashingB4Two pointersB5Sliding windowB6Binary searchB7Sorting algorithmsB8Stacks & queuesB9Linked listsB10Basic recursion
Intermediate12›
I1TreesI2Tree problems in depthI3Heaps & priority queuesI4Graphs: representationI5Graph problemsI6BacktrackingI7DP: 1DI8DP: 2DI9Greedy algorithmsI10IntervalsI11Bit manipulationI12Matrix problems
Advanced12›
A1Advanced DPA2Union-FindA3Advanced graph algorithmsA4Minimum Spanning TreeA5TriesA6Segment & Fenwick treesA7String algorithmsA8Monotonic stack & queueA9Design problemsA10Advanced backtrackingA11Topological patternsA12Interview strategy
/ search[ ] chaptert top

DSA in JS levels

1Beginner2Intermediate3Advanced

Ready to read

JSJavaScript⑂Git◎Interview prepΣDSA in JSSDSystem Design
More topics15›
</>HTML{ }CSS⚛ReactNNext.jsNeNest.jsTSTypeScriptNoNode.js🐳DockerDBSQL & Databases✓Testing🔒Web Security☁Cloud & DevOps◈GraphQL◆Redis☸Kubernetes
100%
I3

Heaps & priority queues

You don't need the whole thing sorted — you need the extreme value, fast, repeatedly.

The problem a heap exists to solve

If you need the minimum (or maximum) value once, scan the array — O(n). If you need it repeatedly, while the data keeps changing, sorting every time is O(n log n) per query — wasteful. A heap gives you the extreme value in O(1) and lets you add or remove in O(log n), which is the sweet spot for "keep asking me for the biggest one" problems.

The one rule: parent beats children

2 5 4 9 7 8 root = the minimum, always 2 5 4 9 7 8 ← stored flat: child of i is 2i+1, 2i+2
Min-heap: every parent ≤ its children. No claim about left vs right — only up vs down.

A heap is not sorted, and it's not a BST — a node's left child can be bigger or smaller than its right child, the only guarantee is parent-vs-children. That weaker guarantee is exactly what makes insert and remove-min cheaper than keeping the whole thing sorted.

Sift-up (insert) and sift-down (remove) — the two moves

Insert always adds at the very end of the array, then "bubbles" it up while it's smaller than its parent. Removing the min always takes the last element, drops it at the root, then "sinks" it down while it's bigger than its smallest child. Both are O(log n) because they only ever travel the height of the tree.

class MinHeap {
  #data = [];

  peek() { return this.#data[0]; }
  size() { return this.#data.length; }

  push(val) {
    this.#data.push(val);
    this.#siftUp(this.#data.length - 1);
  }

  pop() {
    const min = this.#data[0];
    const last = this.#data.pop();
    if (this.#data.length > 0) {
      this.#data[0] = last;
      this.#siftDown(0);
    }
    return min;
  }

  #siftUp(i) {
    while (i > 0) {
      const parent = Math.floor((i - 1) / 2);
      if (this.#data[parent] <= this.#data[i]) break;
      [this.#data[parent], this.#data[i]] = [this.#data[i], this.#data[parent]];
      i = parent;
    }
  }

  #siftDown(i) {
    const n = this.#data.length;
    while (true) {
      let smallest = i;
      const left = 2 * i + 1, right = 2 * i + 2;
      if (left < n && this.#data[left] < this.#data[smallest]) smallest = left;
      if (right < n && this.#data[right] < this.#data[smallest]) smallest = right;
      if (smallest === i) break;
      [this.#data[i], this.#data[smallest]] = [this.#data[smallest], this.#data[i]];
      i = smallest;
    }
  }
}
⚠ JS has no built-in heap — say so, then build one Unlike Python (heapq) or Java (PriorityQueue), JavaScript has no native heap. In an interview, name this explicitly and either implement a small one (above) or, if allowed, describe using a sorted-insert array for small n while stating the tradeoff clearly.

The top-K pattern

This is where heaps earn their keep: finding the k largest elements out of n. Sorting everything is O(n log n). A heap does it in O(n log k) — and when k is small relative to n, that's a real win.

// k largest elements — keep a MIN-heap of size k (counter-intuitive but correct)
function kLargest(nums, k) {
  const heap = new MinHeap();
  for (const num of nums) {
    heap.push(num);
    if (heap.size() > k) heap.pop(); // evict the smallest — keep only the top k
  }
  return heap; // contains exactly the k largest, unsorted among themselves
}

The trick that trips people up: for "k largest," you use a min-heap, not a max-heap — because you want to cheaply evict the smallest of your current top-k candidates the moment a bigger one shows up. The heap's root is always "the next one to kick out," which is the smallest of the keepers.

Say it like this → "I only need the k largest, not a full sort, so I'll keep a min-heap of size k — every new element either gets discarded or bumps out the current smallest keeper, which is O(log k) per element instead of O(n log n) for a full sort."

Building a heap from an array in O(n), not O(n log n)

Pushing n elements one at a time costs O(n log n) — each push is O(log n). But if you already have the full array upfront, you can build the heap faster: place all elements as-is, then sift-down starting from the last non-leaf node backward to the root.

function heapify(arr) {
  const n = arr.length;
  // last non-leaf node is at index Math.floor(n/2) - 1 — every index after that is a leaf
  for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
    siftDown(arr, i, n);
  }
  return arr;
}
⚠ Why this is O(n), not O(n log n) — worth being able to explain Most nodes are near the bottom of the tree, where sift-down has almost no distance to travel. Only the few nodes near the root can sift all the way down. Summing "number of nodes at each level × how far they can sift" across the whole tree converges to O(n), not O(n log n) — a genuinely surprising result that's worth knowing exists, even if you never re-derive the summation live in an interview.

The two-heap pattern — running median of a data stream

A single heap gives you the min or the max. Finding the median of a growing stream needs both at once — the classic trick is to split the data across two heaps that meet in the middle.

max-heap: smaller half e.g. {1, 3, 5} root = 5 (biggest of small half) min-heap: larger half e.g. {7, 9} root = 7 (smallest of big half) median = 5 (odd count) or avg(5, 7) if both heaps were equal-sized
Both roots sit right at the midpoint — the median is always O(1) to read once the split is balanced.
class MedianFinder {
  #small = new MaxHeap(); // same MinHeap code, comparisons flipped — holds the smaller half
  #large = new MinHeap(); // holds the larger half

  addNum(num) {
    this.#small.push(num);
    this.#large.push(this.#small.pop()); // always route through #small first, then rebalance

    if (this.#small.size() < this.#large.size()) {
      this.#small.push(this.#large.pop()); // keep #small equal-or-one-more than #large
    }
  }

  findMedian() {
    if (this.#small.size() > this.#large.size()) return this.#small.peek();
    return (this.#small.peek() + this.#large.peek()) / 2;
  }
}

Every insert is O(log n), and reading the median is O(1) — compare that to re-sorting on every insert (O(n log n) each time) or inserting into a sorted array (O(n) shifting each time). The two-heap split is what makes a streaming median tractable at all.

See it sink

Watch the last element take the root's place and then sink. It only ever follows the smaller child, so it touches one node per level — that is the log n.

Heap — sift-down after extract-min
As a tree, level by level

Recognizing it in an unseen problem

  • "Top K," "k-th largest/smallest," "k closest points"
  • Merging k sorted lists/arrays — a heap tracks "the smallest unmerged element" across all of them
  • You need repeated access to a min/max while the data set keeps changing (a scheduler, a running median)
  • "Running median," "median of a stream" → the two-heap pattern specifically
  • A brute force would re-sort after every update — that's the tell a heap should replace it
Practice this layer

Opens in the editor — write it, run it, and check it against real tests.

Kth Largest Element in an Array5 tests · intermediateFind Median from Data Stream5 tests · advancedK Closest Points to Origin5 tests · intermediateLast Stone Weight5 tests · beginnerTask Scheduler5 tests · advancedSort Characters By Frequency5 tests · intermediateReorganize String5 tests · advancedSmallest Range Covering Elements from K Lists5 tests · advancedFind K Pairs with Smallest Sums5 tests · advancedIPO — Maximise Capital5 tests · advancedMaximum Performance of a Team5 tests · advancedMinimum Cost to Connect Sticks5 tests · intermediateFurthest Building You Can Reach5 tests · advanced
←previousTree problems in depth↑ CovernextGraphs: representation→