Problem
LeetCode 2360 — Longest Cycle in a Graph
You are given a directed graph of n nodes where each node has at most one outgoing edge, represented by edges[] where edges[i] is the next node from i, or -1 if none. Return the length of the longest cycle, or -1 if no cycle exists.
Example
edges = [3, 3, 4, 2, 3]
0 → 3 → 2 → 4 → 3 (cycle: 3 → 2 → 4 → 3, length 3)
1 → 3 (enters the already-processed cycle, no new cycle)
output = 3
Approach — Step Tracking (Functional Graph)
Since each node has at most one outgoing edge, following edges from any node creates a single unbranching path — this is called a functional graph. Every connected component is a rho (ρ) shape: a tail leading into a cycle.
Key insight: no branching means no recursion needed — just a while loop. Stamp each node with a global steps counter when first visited. A single visited[] array replaces both the “on current path” check and the “already processed” check:
visited[node] >= start→ node is on the current path → cycle! length =steps - visited[node]visited[node] < start→ node from a previous traversal → stop, no cycle herevisited[node] == 0→ unvisited → keep walking
Walkthrough
edges = [3, 3, 4, 2, 3], steps starts at 1
Start node 0, start = 1:
visit 0 → visited[0]=1, steps=2, follow to 3
visit 3 → visited[3]=2, steps=3, follow to 2
visit 2 → visited[2]=3, steps=4, follow to 4
visit 4 → visited[4]=4, steps=5, follow to 3
node 3: visited[3]=2 >= start(1) → cycle! length = 5 - 2 = 3
Start node 1, start = 5:
visit 1 → visited[1]=5, steps=6, follow to 3
node 3: visited[3]=2 < start(5) → previous traversal, stop
Complexity
- Time:
O(n)— each node is stamped exactly once - Space:
O(n)— visited array
Solution
class Solution {
public int longestCycle(int[] edges) {
int n = edges.length;
int[] visited = new int[n];
int steps = 1;
int result = -1;
for (int i = 0; i < n; i++) {
if (visited[i] != 0) continue;
int start = steps;
int node = i;
while (node != -1 && visited[node] == 0) {
visited[node] = steps++;
node = edges[node];
}
if (node != -1 && visited[node] >= start) {
result = Math.max(result, steps - visited[node]);
}
}
return result;
}
}
Why It Teaches You Something
The “stamp with global timer, compare against traversal start” trick is a clean alternative to Tarjan’s low-link for functional graphs. The SCC insight still holds: every SCC with size > 1 in this graph is exactly a cycle, and the longest such SCC is the answer.
The same pattern solves:
| Problem | What changes |
|---|---|
| Find Eventual Safe States (LC 802) | nodes not reaching any cycle are “safe” |
| Course Schedule (LC 207) | general graph, use color states instead |
| Linked List Cycle II (LC 142) | same rho-shape, find cycle start node |