← Daily Logs
Algorithms

Shortest Path (Edge List, BFS)

EasyJul 5, 2026graphbfsshortest-path

`hasPath` answers 'can I get there'; this answers 'how many edges does it take'. The only structural change from a plain BFS reachability check is what rides in the queue — `[node, distance]` pairs instead of bare nodes — so distance falls out of the traversal itself instead of needing a separate levels counter.

The problem

Implement `shortestPath(edges, source, target)` where `edges` is a list of `[a, b]` pairs describing an undirected graph. Return the number of edges on the shortest path from `source` to `target`, or `-1` if `target` isn't reachable. `source === target` is its own case, returning `0` with no traversal needed.

The edge list first has to become an adjacency list — the exact same `createGraphAdjacencyObjectFromEdgeList` from `undirectedPath`, unchanged.

The approach

BFS is the only traversal that gets distance for free: because it explores level by level, the first time `target` is reached is guaranteed to be via a shortest path, so there's no need to compare candidate paths — just return as soon as it's found.

The queue holds `[node, distanceFromSource]` tuples rather than bare nodes. Seeding it with `[source, 0]` and, for every neighbour pushed, enqueuing `[node, distanceFromSource + 1]`, means each queue entry already knows its own distance — no side table mapping nodes to depths, no counting how many nodes are at the current BFS 'level' before advancing.

Same early-return shape as `hasPath`: check `neighbour == target` before marking it visited or pushing it, and return `distanceFromSource + 1` immediately rather than waiting to pop it off the queue on a later iteration.

The solution

js
export function createGraphAdjacencyObjectFromEdgeList(edges) {
    const graph = {}
    for (let edge of edges) {
        const [e1, e2] = edge
        if (!(e1 in graph)) graph[e1] = []
        if (!(e2 in graph)) graph[e2] = []
        graph[e1].push(e2)
        graph[e2].push(e1)
    }
    return graph
}

export function shortestPath(edges, source, target) {
    const graph = createGraphAdjacencyObjectFromEdgeList(edges)
    if (source == target) return 0
    const visited = new Set()
    const q = [[source, 0]]
    while (q.length) {
        const [curr, distanceFromSource] = q.shift()
        for (let node of graph[curr]) {
            if (!visited.has(node)) {
                if (node == target) {
                    return distanceFromSource + 1
                }
                visited.add(node)
                q.push([node, distanceFromSource + 1])
            }
        }
    }
    return -1
}

Time O(V + E)Space O(V)

All entries