Find if Path Exists in Graph
Given an undirected graph as edge pairs and a source and destination, return whether a path exists between them.
Open on LeetCode ↗Intuition
The trap is forgetting the graph is undirected, so building the adjacency list needs both directions added for every edge -- add only one direction and a perfectly valid path silently disappears from the traversal. The other trap is source == destination: that is trivially a valid path of length zero, true even if the graph has no edges at all, so it must be checked before touching any edge data. Once both directions are wired up, a single DFS or BFS from the source that watches for the destination is the whole algorithm.
Plain reachability — DFS or BFS from the source, stopping when the destination appears. The only detail that matters is building the adjacency list in both directions, since the edges are undirected.
Approach
Build the adjacency list both ways
For every edge [a, b], append b to adj[a] AND a to adj[b]. This is undirected -- skipping the second append means edges are only 'visible' from one endpoint, and some valid paths become invisible to the traversal.
Handle source == destination first
If source equals destination, the answer is true immediately, regardless of whether the graph has any edges connecting anything. This is a real edge case, not a formality.
DFS/BFS and stop the moment destination is seen
Otherwise walk the graph from source, marking visited vertices to avoid cycles, and stop as soon as destination is reached. If the traversal exhausts every reachable vertex without ever touching destination, no path exists.
Solution & live demo
Common pitfalls
Adding edges in one direction only
adj[a].append(b)
adj[a].append(b) adj[b].append(a)
An undirected edge must be traversable from either endpoint. Storing one direction makes the graph directed and reports no path whenever the route needs to travel against an edge's insertion order.
Marking visited after recursing
for v in adj[u]:
if not seen[v] and dfs(v): return True
seen[u] = Trueseen[u] = True for v in adj[u]:
Undirected edges are symmetric, so a neighbour immediately recurses back into u. Without the flag already set, that becomes infinite mutual recursion and the stack overflows.
Missing the trivial case
return dfs(source)
if source == destination:
return TrueWhen the two are the same vertex the answer is trivially true, but a DFS that marks before testing may not report it depending on the check's placement. Handling it up front removes the ambiguity.
Edge cases
Return true immediately -- valid even with zero edges.
DFS exhausts its component without finding it -- return false.
Both adj[a].append(b) and adj[b].append(a) are required for correctness.
Harmless -- visited check prevents infinite recursion.