Master the Breadth-First Search algorithm for finding shortest paths in unweighted graphs. Learn how to systematically explore graph levels and track paths efficiently.
Level 0:            A
                ↙   ↓   ↘
Level 1:      B     C      D
            ↙   ↘        ↙
Level 2:   E      F    G
                            | Aspect | Breadth-First Search (BFS) | Depth-First Search (DFS) | 
|---|---|---|
| Data Structure | Queue | Stack (or recursion) | 
| Order of Exploration | Level by level | Path by path | 
| Space Complexity | O(w) where w is the maximum width | O(h) where h is the maximum depth | 
| Time Complexity | O(V + E) | O(V + E) | 
| Best For | Shortest path, nearest neighbor | Path finding, exhaustive search | 
| Applications | Social networks, GPS, web crawling | Puzzles, mazes, topological sorting | 
"""
Performs a breadth-first search on a graph starting from a given vertex
:param graph: dict, adjacency list representation of the graph
:param start: starting vertex
:return: dict with parent pointers, distances, and visited set
"""
def bfs(graph, start):
    from collections import deque
    queue = deque([start])
    visited = set([start])
    parent = {}
    distance = {start: 0}
    while queue:
        vertex = queue.popleft()
        for neighbor in graph[vertex]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)
                parent[neighbor] = vertex
                distance[neighbor] = distance[vertex] + 1
    return { 'parent': parent, 'distance': distance, 'visited': visited }
                            """
Finds the shortest path between two vertices in an unweighted graph
:param graph: dict, adjacency list representation of the graph
:param start: starting vertex
:param end: target vertex
:return: list of vertices forming the path, or None if no path exists
"""
def find_shortest_path(graph, start, end):
    if start == end:
        return [start]
    result = bfs(graph, start)
    parent = result['parent']
    if end not in parent:
        return None
    path = []
    current = end
    while current is not None:
        path.insert(0, current)
        current = parent.get(current)
    return path
                            # Sample graph (adjacency list representation)
graph = {
    'A': ['B', 'C', 'D'],
    'B': ['A', 'E'],
    'C': ['A', 'F'],
    'D': ['A', 'G'],
    'E': ['B'],
    'F': ['C'],
    'G': ['D']
}
# Basic BFS traversal
visited = bfs(graph, 'A')['visited']
print('BFS traversal:', list(visited))  # Output: ['A', 'B', 'C', 'D', 'E', 'F', 'G']
# Find shortest path from 'A' to 'F'
path = find_shortest_path(graph, 'A', 'F')
print('Shortest path from A to F:', path)  # Output: ['A', 'C', 'F']
# Find shortest path from 'E' to 'G'
another_path = find_shortest_path(graph, 'E', 'G')
print('Shortest path from E to G:', another_path)  # Output: ['E', 'B', 'A', 'D', 'G']
                            Implement a modified BFS that can start from multiple source nodes simultaneously.
def multi_source_bfs(graph, sources):
    from collections import deque
    queue = deque(sources)
    visited = set(sources)
    distance = {source: 0 for source in sources}
    while queue:
        vertex = queue.popleft()
        for neighbor in graph.get(vertex, []):
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)
                distance[neighbor] = distance[vertex] + 1
    return { 'distance': distance, 'visited': visited }
                            Find shortest path that satisfies certain conditions (e.g., must pass through specific nodes).
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}
# Find path from A to F that passes through E
def find_path_through_node(graph, start, end, must_pass):
    # Find path from start to must_pass
    path1 = find_shortest_path(graph, start, must_pass)
    # Find path from must_pass to end (excluding must_pass itself)
    path2 = find_shortest_path(graph, must_pass, end)
    if path1 is None or path2 is None:
        return None
    return path1 + path2[1:]
print(find_path_through_node(graph, 'A', 'F', 'E'))  # Output: ['A', 'B', 'E', 'F']
                            Group nodes by their levels (distances) from the start node.
const levels = groupByLevels(graph, 'A');
/*
{
    0: ['A'],
    1: ['B', 'C', 'D'],
    2: ['E', 'F', 'G']
}
*/
                            Note: Previously, this course referenced the CodeSignal Arcade for practice, which is no longer available. The LeetCode problems below follow the same principles and are excellent for practicing BFS algorithms.