bfs shortest path Algorithm

The Breadth-First Search (BFS) shortest path algorithm is a graph traversal technique used to find the shortest path between two nodes in an unweighted or uniformly weighted graph. The algorithm operates by exploring all the vertices of a graph in a breadthward motion, meaning it visits all the neighbors of a particular node before moving on to their neighbors. This ensures that the shortest path between any two nodes is discovered in the least number of iterations. BFS is particularly effective for solving problems that require finding the shortest path in a graph with a large number of vertices and edges. The BFS algorithm begins by initializing a queue with the starting node and marking it as visited. Then, it proceeds through the following steps: dequeue the first node in the queue, examine its neighbors, and enqueue any unvisited neighbors, marking them as visited. This process repeats until the queue is empty or the target node is found. Once the target node is visited, the shortest path can be reconstructed by backtracking from the target to the source node, following the marked nodes. The BFS shortest path algorithm has a time complexity of O(V+E), where V is the number of vertices and E is the number of edges in the graph, making it an efficient approach for finding the shortest path in large and sparse graphs.
"""Breadth-first search shortest path implementations.

    doctest:
    python -m doctest -v bfs_shortest_path.py

    Manual test:
    python bfs_shortest_path.py
"""
graph = {
    "A": ["B", "C", "E"],
    "B": ["A", "D", "E"],
    "C": ["A", "F", "G"],
    "D": ["B"],
    "E": ["A", "B", "D"],
    "F": ["C"],
    "G": ["C"],
}


def bfs_shortest_path(graph: dict, start, goal) -> str:
    """Find shortest path between `start` and `goal` nodes.

        Args:
            graph (dict): node/list of neighboring nodes key/value pairs.
            start: start node.
            goal: target node.

        Returns:
            Shortest path between `start` and `goal` nodes as a string of nodes.
            'Not found' string if no path found.

        Example:
            >>> bfs_shortest_path(graph, "G", "D")
            ['G', 'C', 'A', 'B', 'D']
    """
    # keep track of explored nodes
    explored = []
    # keep track of all the paths to be checked
    queue = [[start]]

    # return path if start is goal
    if start == goal:
        return "That was easy! Start = goal"

    # keeps looping until all possible paths have been checked
    while queue:
        # pop the first path from the queue
        path = queue.pop(0)
        # get the last node from the path
        node = path[-1]
        if node not in explored:
            neighbours = graph[node]
            # go through all neighbour nodes, construct a new path and
            # push it into the queue
            for neighbour in neighbours:
                new_path = list(path)
                new_path.append(neighbour)
                queue.append(new_path)
                # return path if neighbour is goal
                if neighbour == goal:
                    return new_path

            # mark node as explored
            explored.append(node)

    # in case there's no path between the 2 nodes
    return "So sorry, but a connecting path doesn't exist :("


def bfs_shortest_path_distance(graph: dict, start, target) -> int:
    """Find shortest path distance between `start` and `target` nodes.

        Args:
            graph: node/list of neighboring nodes key/value pairs.
            start: node to start search from.
            target: node to search for.

        Returns:
            Number of edges in shortest path between `start` and `target` nodes.
            -1 if no path exists.

        Example:
            >>> bfs_shortest_path_distance(graph, "G", "D")
            4
            >>> bfs_shortest_path_distance(graph, "A", "A")
            0
            >>> bfs_shortest_path_distance(graph, "A", "H")
            -1
    """
    if not graph or start not in graph or target not in graph:
        return -1
    if start == target:
        return 0
    queue = [start]
    visited = [start]
    # Keep tab on distances from `start` node.
    dist = {start: 0, target: -1}
    while queue:
        node = queue.pop(0)
        if node == target:
            dist[target] = (
                dist[node] if dist[target] == -1 else min(dist[target], dist[node])
            )
        for adjacent in graph[node]:
            if adjacent not in visited:
                visited.append(adjacent)
                queue.append(adjacent)
                dist[adjacent] = dist[node] + 1
    return dist[target]


if __name__ == "__main__":
    print(bfs_shortest_path(graph, "G", "D"))  # returns ['G', 'C', 'A', 'B', 'D']
    print(bfs_shortest_path_distance(graph, "G", "D"))  # returns 4

LANGUAGE:

DARK MODE: