Breadth-First Search Algorithm- Complete Implementation Guide

What Is Breadth-First Search (BFS)?

Breadth-First Search is a graph traversal algorithm that explores all nodes at the current depth before moving to nodes at the next depth level. Think of it like ripples spreading outward from a stone dropped in water.

You start at a source node, visit all its neighbors, then visit all their unvisited neighbors, and so on. The queue is the secret weapon here—it keeps track of what comes next.

When BFS Actually Makes Sense

Use BFS when you need to find the shortest path in an unweighted graph. That's the main use case, and it's a damn good one.

Other scenarios where BFS wins:

How BFS Works: The Core Logic

Here's the step-by-step breakdown:

  1. Start by putting the source node in a queue
  2. Mark it as visited
  3. While the queue isn't empty, pop the front node
  4. Process that node
  5. Push all unvisited neighbors into the queue
  6. Mark them as visited
  7. Repeat until queue is empty

The visited set prevents infinite loops in graphs with cycles. Skip this step and you'll loop forever. Don't skip it.

BFS vs Depth-First Search: The Real Differences

Most people get confused here. Here's the truth:

Aspect BFS DFS
Data structure Queue (FIFO) Stack (LIFO)
Traversal pattern Level by level Deep first, backtrack
Shortest path Guaranteed Not guaranteed
Memory usage Wider trees eat memory Deeper trees eat memory
Best for Shortest paths, levels Cycles, paths, connectivity

Pick BFS for shortest path. Pick DFS for memory-efficient deep exploration or when you just need to visit everything.

Implementation in Python

Here's a clean, working BFS implementation you can copy-paste:

from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    visited.add(start)
    
    while queue:
        node = queue.popleft()
        print(node, end=' ')
        
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)

# Example graph (adjacency list)
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}

bfs(graph, 'A')

Output: A B C D E F

The deque from collections is faster than a regular list for pop(0). Use it.

Implementation in JavaScript

function bfs(graph, start) {
    const visited = new Set();
    const queue = [start];
    visited.add(start);
    
    while (queue.length > 0) {
        const node = queue.shift();
        console.log(node);
        
        for (const neighbor of graph[node]) {
            if (!visited.has(neighbor)) {
                visited.add(neighbor);
                queue.push(neighbor);
            }
        }
    }
}

// Example usage
const graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
};

bfs(graph, 'A');

Finding Shortest Path with BFS

This is where BFS shines. Here's how to reconstruct the actual path:

from collections import deque

def bfs_shortest_path(graph, start, end):
    visited = {start}
    queue = deque([(start, [start])])
    
    while queue:
        node, path = queue.popleft()
        
        if node == end:
            return path
        
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append((neighbor, path + [neighbor]))
    
    return None  # No path exists

# Usage
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}

print(bfs_shortest_path(graph, 'A', 'F'))  # ['A', 'C', 'F']

The path tracking adds minimal overhead but gives you the actual route, not just visited nodes.

Time and Space Complexity

These are the numbers you need to know:

BFS visits every vertex and edge exactly once. That's optimal for traversal. The space cost is unavoidable if you want correct results.

Common Mistakes That Will Break Your BFS

BFS on Binary Trees

Trees are just special graphs with no cycles. Level-order traversal is BFS:

from collections import deque

class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None

def level_order(root):
    if not root:
        return []
    
    result = []
    queue = deque([root])
    
    while queue:
        level_size = len(queue)
        level = []
        
        for _ in range(level_size):
            node = queue.popleft()
            level.append(node.val)
            
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        
        result.append(level)
    
    return result

This groups nodes by level. Each iteration processes one entire level before moving down.

When BFS Is the Wrong Tool

BFS isn't always the answer:

Know your constraints. A 10,000-node deep chain will kill BFS's queue but barely fazes DFS.

Grid-Based BFS Problems

Many BFS problems involve grids. Standard pattern:

def bfs_grid(grid, start):
    rows, cols = len(grid), len(grid[0])
    queue = deque([start])
    visited = {start}
    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    
    while queue:
        r, c = queue.popleft()
        
        for dr, dc in directions:
            nr, nc = r + dr, c + dc
            
            if 0 <= nr < rows and 0 <= nc < cols and (nr, nc) not in visited:
                visited.add((nr, nc))
                queue.append((nr, nc))
    
    return visited

This handles the boundary checking and neighbor iteration. Adapt the visited condition based on what counts as "traversable."

Bottom Line

BFS is straightforward: use a queue, mark visited nodes early, and process level by level. It guarantees shortest path in unweighted graphs. That's the entire game.

The implementation is short. The tricky part is recognizing when BFS applies. If you're finding shortest distance, exploring level-by-level, or checking connectivity—BFS is probably your answer.