Watch it run
Play it or step through. Every step says what just happened.
const graph = { A: ["B", "C"], B: ["D"], C: ["D", "E"], D: ["F"], E: ["F"], F: [] };function bfs(start) { const dist = { [start]: 0 }; const queue = [start]; while (queue.length > 0) { const node = queue.shift(); console.log(node, dist[node]); for (const next of graph[node]) { if (!(next in dist)) { dist[next] = dist[node] + 1; queue.push(next); } } }}bfs("A");Queue (frontier)
Discovered but not yet visited; oldest first
Visiting
The node just taken from the queue
Finished
Node = distance from A
Console0 lines
Nothing has run yet. The graph has edges A→B, A→C, B→D, C→D, C→E, D→F and E→F. dist doubles as the set of nodes already discovered.
queue.shift() is fine for a small graph. For millions of nodes, keep a head index instead so dequeuing stays O(1); the whole search is then O(nodes + edges).
queue.shift() is fine for a small graph. For millions of nodes, keep a head index instead so dequeuing stays O(1); the whole search is then O(nodes + edges).
Try it yourself
Change the code, predict the order, then run it for real in your browser.
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.
Storing a graph
An adjacency list maps each node to its neighbours, like the graph object above. It uses space proportional to the number of edges and lets you loop over a node's neighbours directly, which is what searches do. An adjacency matrix, an n × n grid of booleans or weights, answers "is there an edge from u to v?" in O(1) but uses n² space, so it only suits small or dense graphs. Most real graphs (road networks, follower graphs, package dependencies) are sparse, so lists win.
Depth-first search
Swap the queue for a stack (or use recursion) and the search dives down one path before backtracking. DFS doesn't find shortest paths, but the order in which it finishes nodes is very useful. It detects cycles (meeting a node that is still on the current path), and reversing the finish order of a directed acyclic graph gives a topological order, an order in which every task comes after the tasks it depends on. Package managers, build tools and spreadsheet recalculation all need that; Python ships it as graphlib.TopologicalSorter.
Weighted shortest paths
When edges have costs, the node with the fewest hops isn't necessarily the cheapest. Dijkstra's algorithm keeps a tentative distance for every node and always expands the unsettled node with the smallest one, which a priority queue provides. With a binary heap it runs in O((V + E) log V). It requires non-negative weights; with negative edges use Bellman–Ford, and for one target on a map, A* adds an estimate of the remaining distance to search towards the goal first.
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
The direct-looking route home → station → office costs 16 and the park route costs 14; Dijkstra finds 13 through the café and the station. Sorting the frontier on every step keeps the example short; swap in the min-heap from the heaps topic to get the O((V + E) log V) bound.
Go deeper
- Undirected graphs: DFS and BFS (Sedgewick and Wayne, Algorithms 4th ed., section 4.1) ↗algs4.cs.princeton.edu
- Shortest paths (Sedgewick and Wayne, Algorithms 4th ed., section 4.4) ↗algs4.cs.princeton.edu
- graphlib: topological sorting (Python docs) ↗docs.python.org
- Dijkstra's algorithm (Wikipedia) ↗en.wikipedia.org