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%
I4

Graphs: representation & traversal

A tree is a graph with no cycles and one root — now drop both restrictions.

A graph is nodes plus connections, nothing more

Trees have exactly one root and no cycles. A graph relaxes both: any node can connect to any other node, connections can be one-way (directed) or two-way (undirected), and cycles are allowed. That generality is why graphs model almost anything — social networks, road maps, dependency chains, web pages linking to each other.

A B C D E A-B-E-D-A is a cycle — something a tree can never have
No single root, connections in any direction, and a cycle (A→B→E→D→A) — none of these are tree-legal.

Two ways to store one, and when each wins

Adjacency list A → [B, D] B → [A, C, E] C → [B, E] D → [A, E] E → [B, C, D] Adjacency matrix A B C D E A [ 0 1 0 1 0 ] B [ 1 0 1 0 1 ] C [ 0 1 0 0 1 ] D [ 1 0 0 0 1 ]
List: compact, fast to iterate neighbors. Matrix: O(1) "are X and Y connected," O(V²) space.
Adjacency listAdjacency matrix
SpaceO(V + E)O(V²) — wasteful for sparse graphs
"Are X, Y connected?"O(degree of X)O(1)
"Give me all of X's neighbors"O(degree of X) — directO(V) — scan the whole row
Best formost real interview graphs (sparse)dense graphs, or when O(1) edge lookup matters most
// building an adjacency list from an edge list — the shape you'll write constantly
function buildGraph(n, edges) {
  const graph = Array.from({ length: n }, () => []);
  for (const [u, v] of edges) {
    graph[u].push(v);
    graph[v].push(u); // omit this line for a DIRECTED graph
  }
  return graph;
}

DFS — go deep, backtrack when stuck

1st 2nd 3rd dead end — backtrack to find any unvisited neighbor
DFS commits to one path fully before ever considering an alternative.
// recursive DFS — the call stack IS the "backtrack" mechanism, for free
function dfs(graph, start, visited = new Set()) {
  visited.add(start);
  console.log(start);
  for (const neighbor of graph[start]) {
    if (!visited.has(neighbor)) dfs(graph, neighbor, visited);
  }
  return visited;
}

// iterative DFS — same order, explicit stack instead of recursion
function dfsIterative(graph, start) {
  const visited = new Set([start]);
  const stack = [start];
  while (stack.length) {
    const node = stack.pop();
    for (const neighbor of graph[node]) {
      if (!visited.has(neighbor)) {
        visited.add(neighbor);
        stack.push(neighbor);
      }
    }
  }
  return visited;
}

BFS — spread outward, layer by layer

start layer 1 layer 1 layer 1 layer 1 L2 L2 every layer-1 node visited BEFORE any layer-2 node — this is why BFS finds shortest paths
The queue enforces "finish this ring before starting the next" — the source of BFS's shortest-path guarantee.
function bfs(graph, start) {
  const visited = new Set([start]);
  const queue = [start];
  const order = [];
  while (queue.length) {
    const node = queue.shift();
    order.push(node);
    for (const neighbor of graph[node]) {
      if (!visited.has(neighbor)) {
        visited.add(neighbor);   // mark visited when ENQUEUED, not when dequeued
        queue.push(neighbor);
      }
    }
  }
  return order;
}
⚠ Mark visited on enqueue, not on dequeue If you wait to mark a node visited until you dequeue it, the same node can be pushed onto the queue multiple times by different neighbors before it's ever processed — wasted work, and on a graph with cycles it can blow up badly. Mark it the instant it's added to the queue.

DFS vs BFS — the complexity is identical, the use case isn't

DFSBFS
TimeO(V + E)O(V + E)
SpaceO(V) — recursion stack or explicit stackO(V) — the queue, can hold a whole "ring"
Finds shortest path (unweighted)?noyes — guaranteed
Natural for"does a path exist," cycle detection, backtracking-style explorationshortest path, "closest," level-by-level problems
Say it like this → "Both visit every node and edge once, so they're both O(V + E) — the choice isn't about speed, it's about the guarantee I need. BFS explores in strict distance order, so it's the only one of the two that guarantees the first time I reach a node is via a shortest path."

Recognizing it in an unseen problem

  • Input described as nodes/edges, a grid (adjacent cells = edges), or "connections between X and Y"
  • "Shortest path," "fewest steps," "minimum number of moves" on an unweighted graph → BFS
  • "Does a path exist," "all paths," "explore every option" → DFS
  • A 2D grid where you move up/down/left/right is a graph in disguise — each cell is a node, each valid move is an edge
Practice this layer

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

Number of Islands5 tests · beginnerMax Area of Island5 tests · intermediateClone Graph5 tests · intermediateRotting Oranges5 tests · intermediateSurrounded Regions5 tests · intermediate
←previousHeaps & priority queues↑ CovernextGraph problems→