When should you use BFS rather than DFS in a graph problem?
BFS explores nodes in order of distance from the start, so it is the right choice whenever the answer depends on distance: shortest path in an unweighted graph, fewest edges between two nodes, or the order in which things become reachable. DFS goes as deep as possible before backtracking, so it is the right choice for cycle detection, topological sorting, connected components, and exhaustive search such as backtracking.
from collections import deque
def bfs_distances(graph, start):
distance = {start: 0}
queue = deque([start])
while queue:
node = queue.popleft()
for neighbour in graph[node]:
if neighbour not in distance:
distance[neighbour] = distance[node] + 1
queue.append(neighbour)
return distance
graph = {"A": ["B", "C"], "B": ["D"], "C": ["D"], "D": []}
print(bfs_distances(graph, "A"))The output shows A at distance zero, B and C at distance one, and D at distance two, which is the shortest number of edges from A to D.
The mechanism is the order in which the frontier is expanded. BFS keeps a queue of the nodes discovered but not yet processed, and because it always expands the oldest one, every node at distance k is processed before any node at distance k plus one. DFS uses a stack instead, so the newest node is expanded next and the search dives down one branch as far as it can go.
The trade-off to name is memory. BFS holds as many nodes as the widest level of the graph, while DFS holds as many as the longest path, so on a wide shallow graph BFS is expensive and on a narrow deep graph DFS is. That is why the iterative DFS is usually written with an explicit stack rather than recursion, because a recursive DFS can blow the Python call stack on a deep graph.