You are given a directed graph of n
nodes numbered from 0
to n - 1
, where each node has at most one outgoing edge.
The graph is represented with a given 0-indexed array edges
of size n
, indicating that there is a directed edge from node i
to node edges[i]
. If there is no outgoing edge from node i
, then edges[i] == -1
.
Return the length of the longest cycle in the graph. If no cycle exists, return -1
.
A cycle is a path that starts and ends at the same node.
For example:
edges = [3,3,4,2,3]
should return 3
The longest cycle in the graph is the cycle: 2 -> 4 -> 3 -> 2. The length of this cycle is 3, so 3 is returned.
edges = [2,-1,3,1]
should return -1
There are no cycles in this graph.
Constraints:
n == edges.length
2 <= n <= 10^5
-1 <= edges[i] < n
edges[i] != i
## Longest Cycle in a Directed Graph
This problem asks us to find the longest cycle in a directed graph where each node has at most one outgoing edge. If no cycle exists, we should return -1.
### Naive Solution (Brute Force)
A brute-force approach would be to start from each node and perform a Depth-First Search (DFS) to detect cycles. For each node, we track the visited nodes and the current path length. If we encounter a node already in the current path, we have found a cycle and update the maximum cycle length.
```python
def longestCycle_brute_force(edges):
n = len(edges)
max_cycle_length = -1
for start_node in range(n):
visited = [False] * n
path = []
def dfs(node):
nonlocal max_cycle_length
if node == -1:
return
if visited[node]:
return
if node in path:
cycle_length = len(path) - path.index(node)
max_cycle_length = max(max_cycle_length, cycle_length)
return
visited[node] = True
path.append(node)
dfs(edges[node])
path.pop()
dfs(start_node)
return max_cycle_length
Time Complexity: O(N^2) in the worst case, where N is the number of nodes. For each node, we potentially traverse all other nodes.
Space Complexity: O(N) due to the visited
array and the path
list.
A more efficient solution involves tracking the distance from the starting node for each path. We use a visited
array to mark nodes visited during the DFS and a dist
array to track the distance from the starting node. When we encounter a node already in the current path, we can calculate the cycle length as the difference between the current distance and the distance when we first visited that node.
def longestCycle(edges):
n = len(edges)
max_cycle_length = -1
visited = [False] * n
for start_node in range(n):
if visited[start_node]:
continue
dist = [-1] * n
curr = start_node
count = 0
while curr != -1 and not visited[curr]:
visited[curr] = True
dist[curr] = count
count += 1
curr = edges[curr]
if curr != -1 and dist[curr] != -1:
cycle_length = count - dist[curr]
max_cycle_length = max(max_cycle_length, cycle_length)
return max_cycle_length
Explanation:
Initialization:
max_cycle_length
is initialized to -1.visited
array to keep track of visited nodes during DFS.Outer Loop:
Inner Loop (DFS):
dist
array to store the distance from the starting node.curr
represents the current node.count
represents the current distance from the starting node.edges
array.Cycle Detection:
curr
is not -1 and dist[curr]
is not -1, it indicates a cycle is found.count - dist[curr]
. This is the difference between the current distance and the distance when we first visited that node in the current path.max_cycle_length
if a longer cycle is found.Return Result:
visited
array takes O(N) space.dist
array takes O(N) space.edges
array is empty, there are no cycles, so the function should return -1.edges[i] == i
for some node i
, there is a self-loop of length 1. This needs to be correctly identified and handled. However, the problem states edges[i] != i
so this will not happen.2 <= n <= 10^5
.