3.5.4 · D5Graphs
Question bank — BFS — algorithm, queue-based, O(V+E), shortest path in unweighted graphs
True or false — justify
BFS always visits nodes in non-decreasing order of their distance from the source.
True — the queue holds at most two consecutive levels and with level- ahead, so dequeues come out sorted by distance, which is the whole reason first arrival is shortest.
If you replace the FIFO queue with a LIFO stack, BFS still finds shortest paths, just in a different order.
False — a stack makes it dive deep (that becomes DFS); first arrival is no longer guaranteed to be via the fewest edges.
On an unweighted graph, the BFS tree edges form a shortest path to every reachable node.
True — each node stores the
parent that first discovered it at distance , so walking parents to the root gives one valid fewest-edge path.BFS gives the unique shortest path to every node.
False — it gives one shortest path; if two equal-length routes exist, BFS keeps only whichever neighbor discovered the node first, ignoring the tie.
Running BFS on a weighted graph where all weights equal 5 still yields correct shortest paths.
True — if every edge has the same positive weight, fewest edges equals least total weight, so BFS's edge-count minimization coincides with true cost (just multiply distances by 5).
The time complexity is because for each vertex we scan its edges.
False — edge scans across all vertices sum to (undirected), and vertices are handled once, so the two loops add to , not multiply.
If the graph is disconnected, a single BFS from one source still labels every node.
False — BFS only reaches nodes in the source's connected component; unreached nodes keep distance "infinity" and must be found by restarting BFS from each unvisited node.
On a directed graph, dist[v] from BFS equals the fewest directed edges from to .
True — BFS respects edge direction (it scans
adj[u] = out-neighbors), so the count is of directed hops; the reverse direction may be unreachable, which is expected.Marking a node visited at dequeue time instead of enqueue time gives the same distances, only slower.
Mostly true on distances but risky — the same node can sit in the queue several times, and if you (correctly) skip already-processed pops you get right answers but queue bloat; the safe convention is mark-at-enqueue.
Spot the error
"I mark a node visited only when I pop it, so I don't waste a visited flag on nodes I haven't processed."
The same unvisited node can be pushed by several neighbors before it is ever popped, so the queue can hold up to duplicates, breaking the "each vertex enqueued once" guarantee — mark visited the instant you push.
"BFS finds the shortest path, and I need the cheapest route on my road map with distances, so BFS is perfect."
BFS minimizes the number of edges, not the summed weight; on a weighted map fewer roads can cost more, so this needs Dijkstra (non-negative weights) instead.
"I skip the visited check inside the neighbor loop — updating dist[w] again is harmless."
Re-enqueueing an already-visited node in a cyclic graph can loop forever and may overwrite a correct smaller distance with a larger one; only enqueue unvisited neighbors.
"I set dist[w] = dist[u] + 1 when I dequeue w, using whichever u I happen to be at."
The distance must be fixed at discovery (enqueue) time from the node that first found
w; setting it at dequeue from a random current node has no meaning — w was already given its correct distance when pushed."For an undirected edge u-v I only add v to u's list, since the edge is symmetric anyway."
An undirected edge must appear in both adjacency lists (
v in adj[u] and u in adj[v]); otherwise BFS from v can never traverse back to u and distances break."My queue is a plain Python list and I use queue.pop() to remove nodes."
list.pop() removes the last element (LIFO), turning your BFS into a DFS-like traversal; you need popleft() (a collections.deque) to preserve FIFO order."I check if not visited[w] but I forgot to set visited[s] = True before the loop."
The source can then be re-discovered by a neighbor and re-enqueued, causing an extra pass; always mark the source visited (and give it
dist = 0) before the main loop starts.Why questions
Why must the source be marked visited and given distance 0 before the loop, not inside it?
Distance to itself is 0 by definition and marking it up front stops a neighbor from re-discovering and re-queueing it, keeping the "each vertex once" invariant intact.
Why does BFS scan edges rather than checking all pairs of vertices to find neighbors?
An adjacency list lets you visit only the real neighbors of
u, giving work; scanning all pairs (matrix style) would cost regardless of how sparse the graph is.Why can the shortest path be reconstructed by walking parent pointers backward?
parent[w] records who discovered w on the shortest-path tree, so the chain from w to s is a fewest-edge route; you reverse it only because you built it from target to source.Why does BFS produce a tree even though the graph has cycles?
Each node is discovered exactly once (marked at enqueue), so it gets exactly one parent; the discovery edges form a tree, and the non-tree ("cross"/"back") edges are simply ignored for distance purposes.
Why is BFS the natural way to 2-color a graph for a bipartite check?
BFS lays nodes out in levels, and coloring by level parity (even vs odd distance) is exactly a 2-coloring; a same-color edge between two nodes on the same level reveals an odd cycle, so the graph is not bipartite.
Why does adding all of a node's neighbors before moving on guarantee we never "miss" a shorter route later?
Because
u was dequeued with the minimum remaining distance, any other route to a neighbor w passes through a node of distance , giving length — so is already optimal.Edge cases
BFS on a graph with a single node and no edges — what happens?
The source is enqueued, popped, has no neighbors, and the queue empties; result is
dist = {s: 0}, the trivial correct answer.BFS from a source that has a self-loop s-s.
When scanning
s's neighbors we find s, but it is already marked visited, so it is skipped — the self-loop harmlessly contributes nothing to distances.BFS on a graph with parallel (multiple) edges between the same two nodes.
The second and later parallel edges hit an already-visited endpoint and are skipped; distances are unaffected because BFS cares only about reachability at fewest hops, not edge multiplicity.
BFS where the target is unreachable from the source.
The target is never enqueued, so its
dist stays at the "infinity"/-1 sentinel; you must initialize distances to that sentinel and interpret it as "no path exists".BFS on a complete graph (every node adjacent to every other) — what are the distances?
Every non-source node is a direct neighbor of
s, so all of them get distance 1 in the first level; the queue swells to nodes at once, illustrating the bound with .BFS on a long path graph from node 0 — how full does the queue get?
Never more than one or two nodes at a time, since each level has a single node; this is the opposite extreme of and shows queue size depends on the graph's "width", not just .
If the source distance is initialized to 0 but you forget to enqueue the source, what breaks?
The main loop starts with an empty queue and exits immediately, leaving every other node unvisited — the source's distance is correct but nothing else is ever explored.
For an unweighted shortest path tree rooted at s, are all leaves at the same depth?
Not necessarily — leaves are simply nodes with no undiscovered neighbors, so different branches can end at different levels depending on where cycles close off further exploration.