Does the Road Network Contain a Loop
Solve this ProblemYou are given the road network of a region as an adjacency list of an undirected graph. Decide whether there is a loop: a closed route through at least three towns that does not use the same road twice.
A depth-first search that remembers where it came from finds a loop the moment it meets an already visited town by a different road.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ n ≤ 10 nodes numbered 0 … n-1; graph[u] lists, in increasing order, every neighbour of u (adjacency-list form of an undirected graph) - ◆
If v is in graph[u] then u is in graph[v]; there are no self-loops and no repeated edges - ◆
The graph may be disconnected - ◆
A loop is a path that starts and ends at the same node, uses at least 3 nodes and never repeats an edge; return true if the graph contains one
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Remove Each Road and See if Its Ends Stay Connected
BruteA road lies on a loop exactly when its two ends are still connected after that road is taken away (the other route is the rest of the loop). So for every road (u, v), run a breadth-first search from u that is not allowed to use that road, and see whether it reaches v. If it does for any road, the graph has a loop. E roads, each costing one search: O(E · (n + E)).
O(E · (n + E))O(n)1class Solution {
2 private boolean connectedWithout(int[][] graph, int a, int b) {
3 boolean[] seen = new boolean[graph.length];
4 Deque<Integer> queue = new ArrayDeque<>();
5 seen[a] = true;
6 queue.add(a);
7 while (!queue.isEmpty()) {
8 int u = queue.poll();
9 if (u == b) return true;
10 for (int w : graph[u]) {
11 if (u == a && w == b) continue;
12 if (!seen[w]) {
13 seen[w] = true;
14 queue.add(w);
15 }
16 }
17 }
18 return false;
19 }
20
21 public boolean hasLoop(int[][] graph) {
22 for (int u = 0; u < graph.length; u++) {
23 for (int v : graph[u]) {
24 if (u < v && connectedWithout(graph, u, v)) return true;
25 }
26 }
27 return false;
28 }
29}Optimal — Depth-First Search Remembering the Parent
OptimalExplore with a depth-first search and remember the node you came from (the parent). Every neighbour is either the parent (just the road you arrived by, ignore it), an unvisited node (go deeper), or a node that was ALREADY visited and is not the parent. The last case means there are two different ways to reach it, which is exactly a loop. Do this from every unvisited node to cover all separate groups. Each road is seen twice: O(n + E).
O(n + E)O(n)1class Solution {
2 private boolean explore(int[][] graph, int node, int parent, boolean[] seen) {
3 seen[node] = true;
4 for (int next : graph[node]) {
5 if (next == parent) continue;
6 if (seen[next]) return true;
7 if (explore(graph, next, node, seen)) return true;
8 }
9 return false;
10 }
11
12 public boolean hasLoop(int[][] graph) {
13 boolean[] seen = new boolean[graph.length];
14 for (int start = 0; start < graph.length; start++) {
15 if (!seen[start] && explore(graph, start, -1, seen)) return true;
16 }
17 return false;
18 }
19}