Home/Pathfinding/BFS on the real map
Search algorithmsCTRL K

BFS on the real map

· Cascavel streets · fewest intersections, not metres
/pathfinding/real-map-bfs
EXPANDED
intersections closed
QUEUE
intersections waiting
HOPS
intersections from the start
ROUTE
length once found
STRAIGHT LINE
start to goal
explored streets · opencurrentroutestart / goal
Click the map: first the start, then the goalLoading the street map…
step 0 / 0
Route (×100 m)20
route_bfs.ts
1
function route(map: RoadMap, start: Node, goal: Node) {
2
  const queue = [start], hops = new Map([[start, 0]]), parent = new Map();
3
  while (queue.length > 0) {
4
    // the oldest intersection in the queue
5
    const current = queue.shift()!;
6
    if (current === goal) return rebuild(parent, goal);
7
    for (const street of map.streetsFrom(current)) {
8
      const next = street.otherEnd(current);
9
      const candidate = hops.get(current)! + 1;
10
      if (hops.has(next)) continue;
11
      hops.set(next, candidate); parent.set(next, current);
12
      queue.push(next);
13
    }
14
  }
15
}
CURRENT STEP
line 1

Loading the street map…

// how it works

Understanding BFS on the real map

BFS · counts intersections, so long blocks fool it
01

The idea

Breadth-first search treats every street segment as one hop, whatever its length. It expands the intersections in rings: all the ones one hop away, then two, then three, and stops when the goal is dequeued. The route it returns crosses the fewest intersections.

That is not the shortest route in metres. A few long blocks along an avenue count the same as many short ones downtown, so BFS happily takes the long way round. Compare its route length with Dijkstra's on the same start and goal: the difference is the price of ignoring the weights.

02

The four stages

1
placetwo intersections about n × 100 m apart
2
dequeuethe oldest open intersection
3
enqueueunseen neighbours, one hop further
4
routefollow parents back along the streets
03

Complexity

best
O(d)the goal next door
average
O(V + E)rings of intersections until the goal
worst
O(V + E)the whole map
space
O(V)queue and parents
04

Versus its siblings

ROUTE LENGTH · CASCAVEL, 3.2 km STRAIGHT
bfs
4,550
dijkstra
4,280
a*
4,280
same start and goal (seed 7, route 30) · BFS's route has fewer intersections and 270 m more asphalt
05

Pseudocode

ROUTE(map, start, goal)
  queue ← [start]; hops[start] ← 0
  while queue not empty
    current ← dequeue the oldest
    if current = goal: return the route via parent
    for each street leaving current, to an unseen neighbour
      hops[neighbour] ← hops[current] + 1; parent[neighbour] ← current; enqueue neighbour
06

When to use

Any road, rail or pipe network with lengths on the segments: GPS routing, logistics, network packets.
Rerun on a longer route (the slider) to watch the gap between the three searches open up.
07

Pitfalls

It answers 'fewest turns', not 'shortest': fine for a subway map, wrong for driving.
Marking an intersection when it is enqueued, not when dequeued, is what keeps the queue small.
One-way streets make the graph directed; the return trip can be a different route.
08

History

The map is the centre of Cascavel, Paraná: 4 180 intersections and 5 438 street segments from OpenStreetMap, one-way streets respected, about 10 by 7 km. Every real routing engine runs this same loop on a graph a million times larger, with contraction hierarchies or landmarks precomputed so that a query touches only a sliver of it.

Back to Pathfinding