Back to Graph Theory Series

Multi-Source BFS

October 4, 2026 Wasil Zafar 18 min read

"What's the shortest distance to the nearest of these five hospitals?" Running BFS separately from every source and taking the minimum wastes time recomputing shared work. Multi-source BFS solves it in one pass: seed the queue with every source simultaneously, and let the frontier expand outward from all of them at once.

Contents

  1. A Bit of History
  2. Working Principle: The Virtual Super-Source
  3. Worked Example & SVGs
  4. Mathematical Formulation
  5. Complexity Analysis
  6. Implementation
  7. Real-World Applications
  8. Exercises
  9. Limitations

A Bit of History

Multi-source BFS is a direct extension of Edward F. Moore's 1959 breadth-first search — the innovation is not a new traversal rule but a new way of thinking about the "start" of the search. The technique became especially popular through competitive programming communities in the 2000s and 2010s as a standard pattern for "nearest facility" style problems, and is formally equivalent to adding a single virtual super-source vertex connected to every real source with zero-weight edges — a trick with roots in classical network-flow reductions (Ford & Fulkerson, 1956) where multiple sources/sinks are similarly unified into one.

Intuitive Analogy: Ripples in a Pond Imagine dropping three pebbles into a quiet pond at the exact same second. Instead of tracking the circular waves generated by each pebble independently, you watch the combined wave pattern expand outward. Where two wavefronts collide, the area has already been reached by whichever pebble was closer! Multi-source BFS behaves like these simultaneous expanding ripples, covering the entire graph in a single unified wave expansion.
Pond Ripple Analogy: Simultaneous Wavefront Collision in Multi-Source BFS Source 1 Source 2 Source 3 Wave Front Collision Visited by closest source first! Each concentric ring represents a BFS distance level ($t=0, t=1, t=2$). Nodes are finalized at the exact moment the nearest wavefront reaches them.
Graphical depiction of three simultaneous expanding wavefronts colliding in a single multi-source traversal.

Working Principle: The Virtual Super-Source

Rather than running $k$ separate BFS traversals (one per source) and taking a pointwise minimum — costing $O(k \cdot (V + E))$ — multi-source BFS achieves the same result in a single $O(V + E)$ pass:

  • Multi-Source Initialization: Initialize the BFS queue with all source vertices simultaneously, each tagged with distance 0. Mark all initial sources as visited immediately.
  • Unified Queue Traversal: Proceed with standard BFS exactly as usual — pop from the front, inspect unvisited neighbors, assign dist[neighbor] = dist[u] + 1, and push to the back.
  • Earliest Arrival Guarantee: Because every source starts at distance 0 and BFS explores level-by-level, the first time any vertex $v$ is reached, it is guaranteed to be reached via the closest source.

Why This Works: The Super-Source Reduction

This is mathematically identical to adding one new virtual vertex $s^*$ connected by zero-weight directed edges to every real source $s_i \in S$, then running ordinary single-source BFS from $s^*$. Since $s^*$'s neighbors all start at distance 1 from $s^*$ (which maps to distance 0 from the original sources), seeding the queue directly with all sources at distance 0 is completely equivalent — without allocating an extra vertex or modifying edge lists.

Theoretical Model: Virtual Super-Source s* s* Phantom Node w=0 w=0 S1 S2 A B Single-source BFS from s* gives exact shortest dists Practical Method: Seed Queue with {S1, S2} Queue: S1 (d=0) S2 (d=0) S1 S2 Simultaneous expansion in O(V + E) time!
Comparison between the theoretical zero-weight super-source model and the practical queue-seeding implementation.

Worked Example & SVGs

Consider the classic Rotten Oranges problem on a $3 \times 3$ grid:

  • Grid values: 2 = Rotten (Source), 1 = Fresh, 0 = Empty.
  • Initial state: [ [2, 1, 1], [1, 1, 0], [0, 1, 2] ] with sources at $(0, 0)$ and $(2, 2)$.
Step-by-Step Multi-Source Expansion on a 3x3 Grid t = 0 (Initial Seed) Queue: [(0,0), (2,2)] 2 Rotten 1 Fresh 1 Fresh 1 Fresh 1 Fresh 0 Empty 0 Empty 1 Fresh 2 Rotten Wave 1 t = 1 (First Pass) 3 Cells Newly Rotted 2 2 +NEW 1 2 +NEW 1 0 0 2 +NEW 2 Wave 2 t = 2 (Final State) All Reachable Rotted! 2 2 2 +NEW 2 2 +NEW 0 0 2 2 Rotten Cell (2) Fresh Cell (1) Empty Cell (0) Newly Rotted in Step Result: All fresh oranges are rotted simultaneously at Minute 2. Total Time Elapsed = 2.
Grid propagation visualization highlighting newly infected cells (+NEW) during multi-source expansion layer by layer.

Mathematical Formulation

Let $G = (V, E)$ be an unweighted graph, and let $S \subseteq V$ be the non-empty set of source vertices. The distance to the nearest source $\text{dist}(v, S)$ for any vertex $v \in V$ is defined as:

$$\text{dist}(v, S) = \min_{s \in S} d(s, v)$$

where $d(s, v)$ denotes the standard unweighted shortest path distance (number of edges) from $s$ to $v$. Multi-source BFS guarantees that when a vertex $v$ is popped from the queue, its assigned distance equals $\text{dist}(v, S)$.

Complexity Analysis

Let $V$ be the number of vertices and $E$ be the number of edges:

$$\text{Time Complexity: } O(V + E) \qquad \text{Space Complexity: } O(V)$$

Notice that the number of source vertices $k = |S|$ does not affect the asymptotic time complexity! Running $k$ independent single-source BFS passes would require $O(k \cdot (V + E))$ time, whereas multi-source BFS performs the exact same computation in a single linear pass.

Approach Queue Seeding Time Complexity Space Complexity
Naive Repeats Run single-source BFS $k$ times $O(k \cdot (V + E))$ $O(V)$
Multi-Source BFS Seed queue with all $k$ sources at $t=0$ $O(V + E)$ $O(V)$
Super-Source Reduction Add $s^*$, add $k$ zero-weight edges $O((V+1) + (E+k)) = O(V + E)$ $O(V + k)$

Implementation

Below are production-ready multi-source BFS implementations in Python, C++, and Java:

from collections import deque

def multi_source_bfs(n, adj, sources):
    """
    n: number of vertices
    adj: adj[u] = list of neighbors
    sources: list of source vertices, all starting at distance 0
    Returns dist[] = distance to the NEAREST source for every vertex.
    """
    INF = float('inf')
    dist = [INF] * n
    q = deque()

    for s in sources:
        dist[s] = 0
        q.append(s)

    while q:
        u = q.popleft()
        for v in adj[u]:
            if dist[v] == INF:
                dist[v] = dist[u] + 1
                q.append(v)

    return dist

# Example: 6 vertices, sources = {0, 5}
n = 6
adj = [[] for _ in range(n)]
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
for u, v in edges:
    adj[u].append(v)
    adj[v].append(u)

print("Distance to nearest source:", multi_source_bfs(n, adj, sources=[0, 5]))
#include <iostream>
#include <vector>
#include <queue>
#include <limits>

using namespace std;
const int INF = numeric_limits<int>::max();

vector<int> multiSourceBFS(int n, vector<vector<int>>& adj, vector<int>& sources) {
    vector<int> dist(n, INF);
    queue<int> q;

    for (int s : sources) {
        dist[s] = 0;
        q.push(s);
    }

    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int v : adj[u]) {
            if (dist[v] == INF) {
                dist[v] = dist[u] + 1;
                q.push(v);
            }
        }
    }
    return dist;
}

int main() {
    int n = 6;
    vector<vector<int>> adj(n);
    vector<pair<int,int>> edges = {{0,1},{1,2},{2,3},{3,4},{4,5}};
    for (auto& e : edges) {
        adj[e.first].push_back(e.second);
        adj[e.second].push_back(e.first);
    }

    vector<int> sources = {0, 5};
    vector<int> dist = multiSourceBFS(n, adj, sources);

    cout << "Distance to nearest source: ";
    for (int d : dist) cout << d << " ";
    cout << endl;
    return 0;
}
import java.util.*;

public class MultiSourceBFS {
    public static int[] solve(int n, List<List<Integer>> adj, int[] sources) {
        int[] dist = new int[n];
        Arrays.fill(dist, Integer.MAX_VALUE);
        Deque<Integer> q = new ArrayDeque<>();

        for (int s : sources) {
            dist[s] = 0;
            q.add(s);
        }

        while (!q.isEmpty()) {
            int u = q.poll();
            for (int v : adj.get(u)) {
                if (dist[v] == Integer.MAX_VALUE) {
                    dist[v] = dist[u] + 1;
                    q.add(v);
                }
            }
        }
        return dist;
    }

    public static void main(String[] args) {
        int n = 6;
        List<List<Integer>> adj = new ArrayList<>();
        for (int i = 0; i < n; i++) adj.add(new ArrayList<>());

        int[][] edges = {{0,1},{1,2},{2,3},{3,4},{4,5}};
        for (int[] e : edges) {
            adj.get(e[0]).add(e[1]);
            adj.get(e[1]).add(e[0]);
        }

        int[] dist = solve(n, adj, new int[]{0, 5});
        System.out.println("Distance to nearest source: " + Arrays.toString(dist));
    }
}

Real-World Applications

1. Nearest Emergency Facility Location

City GIS mapping engines compute the shortest distance to the nearest hospital or fire station for every residential block. By seeding the queue with all facility nodes, a single multi-source BFS computes distances for the entire metropolitan area in $O(V + E)$ time.

2. Wildfire & Disease Outbreak Propagation

In environmental and epidemiological modeling, multiple initial ignition or infection points expand simultaneously across terrain grids. Multi-source BFS computes the exact arrival time of the wave front at every location.

Exercises

Practice Problems

  1. Solve the "Rotten Oranges" problem on a $5 \times 5$ grid with 3 initially-rotten cells scattered around the border.
  2. Prove formally that multi-source BFS produces the same result as running single-source BFS from each source and taking the pointwise minimum.
  3. Modify the algorithm to return an array nearest_source[] recording which specific source is closest to each vertex.
Hint: Track nearest_source[v] = nearest_source[u] when relaxing edges.

Limitations

Unweighted Graphs Only

Plain queue-based multi-source BFS is valid only on unweighted graphs or uniform-cost grid graphs. For weighted graphs with varying edge costs, replacing the queue with a Priority Queue yields multi-source Dijkstra's algorithm.