← Daily Logs
Algorithms

BFS Graph Traversal

EasyJul 4, 2026graphbfstraversal

Visit a graph level by level with a plain array as a queue — `shift()` off the front, `push()` the neighbours onto the back, and the FIFO order alone guarantees nodes come out in the order they were first discovered.

The problem

Implement `bfs(graph, source)` where `graph` is an adjacency list (`{ node: [neighbours] }`) and `source` is the starting node. Return an array of every node reachable from `source`, in breadth-first order.

For `{ a: ['b', 'c'], b: ['d'], c: ['e'], d: [], e: [] }` starting at `a`, the expected order is `['a', 'b', 'c', 'd', 'e']` — both of `a`'s direct neighbours come out before either of theirs. It also has to cope with disconnected nodes (nodes with no path from `source` just don't appear) and single-node graphs.

The approach

A queue is the whole trick: `q = [source]`, then loop while it's non-empty, `shift()` the front node off, record it in `result`, and `push()` each of its neighbours onto the back. Because `shift()` always takes the oldest entry, every node at depth `k` is dequeued — and its neighbours enqueued — before any node at depth `k+1` gets touched.

There's no visited set here because the test graphs are all trees (each node has exactly one path in from `source`), so nothing gets revisited. On a graph with cycles or shared neighbours this would need a `visited` `Set` checked before pushing, or the same node could be queued twice and `result` would contain duplicates.

The solution

js
export function bfs(graph, source) {
    const q = [source]
    const result = []
    while (q.length) {
        const node = q.shift()
        result.push(node)
        for (let neighbour of graph[node]) {
            q.push(neighbour)
        }
    }
    return result
}

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

All entries