Learning/Graphs

Graphs

13 questions — the largest section after Trees, and the one where recognising which standard traversal applies is most of the work.

Seven of thirteen are grids

A grid is a graph you never have to build
A grid is a graph you never have to build

Each cell is a node; its four neighbours are its edges, computed arithmetically. With V = m·n and E ≤ 4V, every traversal is O(m·n). No adjacency list is ever built.

Choosing the traversal

Question shapeAlgorithmQuestions
Count / measure componentsDFS or BFS flood1, 2, 11
Shortest path, unweightedBFS13
Distance to the nearest of many sourcesmulti-source BFS4, 5
Copy a structure with cyclesDFS/BFS + a map3
Cycle detection, directed3-colour DFS or Kahn's8, 9
Cycle detection, undirectedunion-find, or DFS with a parent check10, 12
Reachability for every cellreverse the search6, 7

DFS vs BFS for pure reachability is a genuine trade: O(depth) stack against O(width) queue. At 300 × 300 an all-land grid is one 90,000-cell island, which overflows the default JVM stack — so BFS is the safe default at that size.

Multi-source BFS

Seed the queue with every source
Seed the queue with every source

Seeding every source at distance 0 turns O(sources · m · n) into O(m · n). It's correct because BFS expands in order of distance from the seed set, so a cell's first arrival is already its minimum — no cell ever needs updating.

Questions 4 and 5 are the same algorithm; only what you record differs (a per-cell distance vs a level count).

Reversing the question

Start from the oceans, not from the cells
Start from the oceans, not from the cells

When "can X reach the target?" must be answered for every X, invert it to "what can the target reach?". One traversal replaces n of them — O((m·n)²)O(m·n).

The reversal needs the relation to be invertible. In Pacific Atlantic, A → B iff height[A] >= height[B], which reads identically backwards. Surrounded Regions applies the same idea to a membership test: "surrounded" is hard to check, "touches the border" is trivial, and everything unmarked is surrounded by definition.

Cycle detection needs three states

Three states, not two
Three states, not two

A plain visited set conflates "on my current path" with "already finished". Only the first is a cycle.

Verified: on the acyclic diamond A→B, A→C, B→C, a two-state check reports a cycle. Three colours correctly report none.

And in an undirected graph the problem is different again — every edge looks like a back-edge to its own parent — so you track the parent or use union-find, which has no notion of direction.

Union-Find

Path compression flattens the tree
Path compression flattens the tree

Two independent optimisations: union by size stops deep chains forming, path compression flattens whatever chains exist. Either alone gives O(log n); together, O(α(n)) — inverse Ackermann, below 5 for any n that fits in the universe. Effectively constant, not literally so.

The same test means different things in three questions:

find(u) == find(v)MeaningQuestion
rejecta cycle — not a tree10
skipa redundant edge; count unchanged11
return itthis is the redundant edge12

The n − 1 edge theorem

A connected graph needs at least n − 1 edges; an acyclic one has at most n − 1. So with exactly n − 1, connected ⟺ acyclic — check either and you've proved both.

But n − 1 edges alone proves nothing: a triangle plus an isolated node has n − 1 edges and is neither connected nor acyclic.

Word Ladder: the adjacency is the bottleneck

BFS gives the shortest path when every edge costs 1
BFS gives the shortest path when every edge costs 1

The BFS is standard. Comparing every pair of words to find neighbours is O(N² · L) = 2.5 × 10^8; bucketing by wildcard pattern (h*t, ho*, *it) is O(N · L²) = 5 × 10^5. Roughly 500×, and it's why the problem is Hard.

The traps

TrapSymptom
DFS on a 300×300 all-land grid (Q1)90,000 frames — stack overflow
Marking visited on dequeue, not enqueueDuplicates in the queue; work multiplies
Not registering the copy before recursing (Q3)Infinite recursion on any undirected edge
Counting -1 for zero fresh oranges (Q5)Should be 0
Missing the level snapshot (Q5)Everything rots in one minute
Inverting the height comparison (Q6)Plausible wrong answer, not a crash
`
Two-state cycle detection (Q8)Reports cycles in DAGs
Iterating edges, not nodes (Q11)Isolated nodes uncounted
int[n] for 1-indexed nodes (Q12)Index out of bounds on node n
Pairwise word comparison (Q13)O(N²·L) — times out

Verification

Every snippet compiled and cross-checked against an independent implementation — 4,300+ randomized cases:

One deliberately-broken variant confirms the headline trap: a two-state cycle check reports a cycle on the acyclic diamond 0→1, 0→2, 1→2.