Opening the reading…
Opening the reading…
GRAPHS › DFS AND BFS ON GRAPHS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →Because every node has at most one outgoing edge, starting from a node gives you one forced path: there are no branching choices. The path either reaches -1, reaches a node already settled by an earlier start, or returns to a node that is still on the current path. Only the third case creates a new cycle.
While following one path, assign its nodes distances 0, 1, 2, and so on from the starting node. If the walk reaches a node already on this same path, the cycle begins at that node's stored distance and ends at the current distance. The difference between those distances is exactly the number of edges in the cycle. Once the path is finished, every node on it can be marked permanently processed, because its outgoing route has already been completely explored.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | Each node becomes active at most once, and each active node is followed once. The cleanup pass also retires each node at most once; nodes already in state 2 are skipped, so the two passes do not create repeated traversal. |
| Space | O(n) extra | The state and distance arrays store one value per node. The returned integer is output rather than working memory, and there is no recursive call stack; the bound remains O(n) even when the graph is one long chain. |
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
public:
int longestCycle(vector<int>& edges) {
int n = edges.size();
vector<int> state(n, 0);
vector<int> dist(n, 0);
int ans = -1;
for (int i = 0; i < n; ++i) {
if (state[i] != 0) {
continue;
}
int cur = i;
int d = 0;
while (cur != -1 && state[cur] == 0) {
state[cur] = 1;
dist[cur] = d++;
cur = edges[cur];
}
if (cur != -1 && state[cur] == 1) {
ans = max(ans, d - dist[cur]);
}
cur = i;
while (cur != -1 && state[cur] == 1) {
state[cur] = 2;
cur = edges[cur];
}
}
return ans;
}
};The two values that make the measurement work are d and dist. At the moment the walk reaches cur again, d is the distance just after the last newly visited node, while dist[cur] is where the cycle started. The cleanup loop is equally important: state 1 is temporary evidence for the current walk, so every such node must become state 2 before another starting node is considered.
You can also remove every node that cannot belong to a cycle. Compute indegrees, place all zero-indegree nodes in a queue, and repeatedly remove them while decreasing the indegree of their only outgoing neighbor. Every removed node is outside a cycle. The nodes left afterward are exactly the disjoint cycles, so you can walk each remaining cycle and count its length.
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
class Solution {
public:
int longestCycle(vector<int>& edges) {
int n = edges.size();
vector<int> indegree(n, 0);
queue<int> q;
for (int node = 0; node < n; ++node) {
if (edges[node] != -1) {
++indegree[edges[node]];
}
}
for (int node = 0; node < n; ++node) {
if (indegree[node] == 0) {
q.push(node);
}
}
while (!q.empty()) {
int node = q.front();
q.pop();
int next = edges[node];
if (next != -1 && --indegree[next] == 0) {
q.push(next);
}
}
int answer = -1;
vector<bool> seen(n, false);
for (int node = 0; node < n; ++node) {
if (indegree[node] == 0 || seen[node]) {
continue;
}
int length = 0;
int cur = node;
do {
seen[cur] = true;
++length;
cur = edges[cur];
} while (cur != node);
answer = max(answer, length);
}
return answer;
}
};This is not asymptotically faster: it is O(n) time and O(n) extra space, like the path-state solution. It buys a clean separation between elimination and measurement, and it avoids storing distances. The state-and-distance walk is usually shorter when you want the cycle length at the exact moment it is detected; pruning is a reasonable alternative when the surviving-cycle viewpoint feels more natural.