Graph connections

Draft

Breadth-First Search

Find shortest paths in an unweighted graph by expanding from the start in distance layers.

algorithm beginner graphsalgorithms

Hook problem

Suppose every edge has the same cost: one step. From A, which nodes are 0, 1, 2, and 3 steps away?

The target result is a set of distance layers from A.BFS graph with distance layers from A.
AA: expandedBB: expandedCC: expandedDD: expandedEE: expandedFF: expandedGG: expanded
Queue-
Current-
Edge-
RuleThe target result is a set of distance layers from A.

First naive idea

You could walk deeply down one path, such as A -> B -> D -> G, before checking nearby alternatives. That can reach a far node while closer options are still waiting.

A deep walk can reach G before closer alternatives are expanded.Deep path A to B to D to G highlighted.
AA: currentBB: unseenCC: unseenDD: unseenEE: unseenFF: unseenGG: unseen

Where it breaks

If discovery order is allowed to go deep, it no longer guarantees nondecreasing distance from the start. The small directed comparison isolates the policy difference: a FIFO queue preserves layers, while a stack-like policy can expose deeper nodes first.

FIFO keeps layers; LIFO can expose deeper nodes first.Queue versus stack comparison.
ACBDEXstack top -> B, then D, then E

The core invention

BFS keeps a queue: discovered nodes wait in first-in, first-out order. When B discovers D and E, C still stays in front of them because C was discovered earlier.

C stays in front of newly discovered D and E.Queue after expanding B shows C before D and E.
AA: expandedBB: currentCC: queuedDD: queuedEE: queuedFF: unseenGG: unseen
QueueC, D, E
CurrentB
Edge-
RuleC stays in front of newly discovered D and E.

Interactive visual demo

AA: currentBB: waitingCC: waitingDD: waitingEE: waitingFF: waitingGG: waiting

Breadth-first search trace

Start at A. It is discovered, queued, and has distance 0.

Queue:A
CurrentA
Active edge-
DiscoveredA
Expanded-
NodeDistanceParent
A0-
B--
C--
D--
E--
F--
G--

Formal version

BFS starts from a source s, discovers undiscovered neighbors, records distance[neighbor] = distance[current] + 1, records parent[neighbor] = current, and enqueues the neighbor.

This page uses discovered or visited for nodes that have already been found and queued. expanded means a node has been dequeued and fully scanned.

A first discovery sets visited, distance, parent, and queue together.Formal BFS state while discovering G from D.
AA: expandedBB: expandedCC: expandedDD: currentEE: queuedFF: queuedGG: queued
QueueE, F, G
CurrentD
EdgeD-G
RuleA first discovery sets visited, distance, parent, and queue together.

Implementation sketch

function bfs(start: string, graph: Record<string, string[]>) {
  const queue = [start];
  let head = 0;
  const visited = new Set([start]);
  const distance: Record<string, number> = { [start]: 0 };
  const parent: Record<string, string | undefined> = { [start]: undefined };

  while (head < queue.length) {
    const current = queue[head++];
    for (const neighbor of graph[current]) {
      if (visited.has(neighbor)) continue;
      visited.add(neighbor);
      distance[neighbor] = distance[current] + 1;
      parent[neighbor] = current;
      queue.push(neighbor);
    }
  }

  return { distance, parent };
}

The visual “dequeue” maps to queue[head++], not queue.shift(), so the TypeScript sketch keeps linear-time queue behavior.

The code block for discovery maps to one visible trace step.Code to trace mapping for discovering G.
AA: expandedBB: expandedCC: expandedDD: currentEE: queuedFF: queuedGG: queued
QueueE, F, G
CurrentD
EdgeD-G
RuleThe code block for discovery maps to one visible trace step.

Correctness intuition

The queue can contain a boundary like [remaining layer k | newly discovered layer k+1]. The key is that all remaining layer-k nodes stay before layer-k+1 nodes, so a farther node cannot expand early.

Queue split: [remaining layer 1 | new layer 2] = [C | D, E].Layer barrier queue after expanding B.
AA: expandedBB: currentCC: queuedDD: queuedEE: queuedFF: unseenGG: unseen
QueueC, D, E
CurrentB
Edge-
RuleQueue split: [remaining layer 1 | new layer 2] = [C | D, E].

Complexity

With an adjacency list, BFS discovers each node once and scans each adjacency row when that node expands. The total time is O(V + E), and the queue, distance table, parent table, and visited set use O(V) space.

Each adjacency row is scanned once, so work is V plus E.BFS edge scan cost checklist.
AA: expandedBB: expandedCC: expandedDD: expandedEE: expandedFF: expandedGG: expanded
Queue-
Current-
Edge-
RuleEach adjacency row is scanned once, so work is V plus E.

Seven nodes are discovered once; eight undirected edges are represented by adjacency rows and scanned from expanded nodes.

Common confusions

Marking a node when it is discovered prevents duplicate queue entries. In the trace, E and F both try G, but G keeps the parent from its first discovery.

Marking on discovery prevents duplicate G entries.Visited timing duplicate G caution.
AA: expandedBB: expandedCC: expandedDD: expandedEE: currentFF: queuedGG: queued
QueueF, G
CurrentE
EdgeE-G
RuleMarking on discovery prevents duplicate G entries.

Delayed marking could create duplicate queue entries like [G, G]. Marking on discovery prevents that.

BFS finds shortest paths by edge count only. If edges have different costs, the fewest-edge path may not be the cheapest path.

Fewest edges and lowest total cost can disagree.Weighted graph where BFS edge count differs from total cost.
1011ABCfewest edges: A-B; lowest cost: A-C-B

Distance is a number. Parent pointers recover an actual path tree.

Distances are numbers; parents recover a path tree.BFS parent tree and distance table.
AA: expandedBB: expandedCC: expandedDD: expandedEE: expandedFF: expandedGG: expanded
Queue-
Current-
Edge-
RuleDistances are numbers; parents recover a path tree.
Final BFS state
NodeDistanceParent
A0-
B1A
C1A
D2B
E2B
F2C
G3D

Connections in the graph

BFS depends on graph basics: it repeatedly asks for the current node’s immediate neighbors. Dijkstra keeps the frontier-and-distance idea, but chooses the next node by smallest total cost instead of queue order.

Exercises

  1. After expanding B, why does C stay before D and E?
  2. When E scans G, why does parent[G] stay D?
  3. What changes if the queue is replaced by a stack?
  4. Why does the weighted triangle require Dijkstra instead of BFS?

Graph connections : Breadth-First Search