"""BFS shortest path (unweighted) with deterministic path reconstruction. Public API: shortest_path(adj, src, dst) -> list[int] | None Returns the vertex sequence from src to dst along a shortest path, or None if dst is unreachable. src==dst returns [src]. shortest_distances(adj, src) -> dict[int, int] Distance in edges from src to every reachable vertex. """ from collections import deque def shortest_distances(adj, src): if src not in adj: # isolated source still reachable to itself pass dist = {src: 0} q = deque([src]) while q: u = q.popleft() for v in adj.get(u, ()): if v not in dist: dist[v] = dist[u] + 1 q.append(v) return dist def shortest_path(adj, src, dst): if src == dst: if src in adj or src in _all_vertices(adj): return [src] # allow unknown isolated vertex only if explicitly present as key? We require vertex existence. return None if src not in _all_vertices(adj) or dst not in _all_vertices(adj): return None parent = {src: None} q = deque([src]) found = False while q: u = q.popleft() if u == dst: found = True break for v in adj.get(u, ()): if v not in parent: parent[v] = u q.append(v) if dst not in parent: return None # reconstruct path = [] cur = dst while cur is not None: path.append(cur) cur = parent[cur] path.reverse() return path def _all_vertices(adj): s = set(adj.keys()) for u, nbrs in adj.items(): for v in nbrs: s.add(v) return s