Skip to main content

2360. Longest Cycle

Longest Cycle in a Graph

A classic cycle detection problem — DFS is the most intuitive way.

One advantage of this problem is that these nn nodes are labeled 00 to n1n - 1 as non-negative integers, so a plain vector<int> works perfectly to track visit orders.

Using unordered_map<int, int> would be much slower......🐢

When Does a Cycle Form? Think About It First

As we recursively traverse this directed graph and arrive at node uu,

if uu has no outgoing edge, it's a dead end — node uu is no part of any cycle.

Now suppose uu has an outgoing edge toward node vv. Node vv has three possible states:

I. Not yet visited 🔴

II. Currently on the recursion stack 🟡

III. Previously visited and already off the stack 🟢

Which state marks the moment a cycle is formed?

The answer is state II — currently on the recursion stack 🟡 That's the moment to celebrate a newly formed cycle 🎊

(u,v)(u, v) is the last piece of puzzle for this new cycle CC 🧩

Node vv is this cycle's start and node uu is the end 🏁. Length of cycle CC is d[u]+1d[v]d[u] + 1 - d[v],

where d[u]d[u] and d[v]d[v] are the visit orders of nodes uu and vv respectively.

Since we are asked to find the longest cycle, whenever a new cycle forms,

we compare d[u]+1d[v]d[u] + 1 - d[v] against the current best and update if it's a new record.

A Handy Tracking Trick

One small detail worth noting: to quickly distinguish between states II and III above,

I like to use different markers in the integer array visitedOrders:

  1. -1: node not yet visited 🔴
  2. Positive integer: node is on recursion stack 🟡. Integer is node's visit order
  3. -2: node has been visited and popped off stack 🟢

DFS_Efficiency Each node and each edge is visited exactly once. Time complexity is O(V+E)O(V + E).

Space complexity is also O(V+E)O(V + E).

#include <vector>
using namespace std;

class LongestCycle // LeetCode Q.2360.
{
private:
vector<int> graph, visitedOrders;
int currentOrder = 1, maxCycle = -1;

void dfsCycle(int node) {
visitedOrders[node] = currentOrder;
currentOrder++; // Increment for the next node.

int tgtNode = graph[node];
if (tgtNode == -1) // No outgoing edge.
{
visitedOrders[node] = -2; // Current node finalizes DFS.
return;
}

if (visitedOrders[tgtNode] == -1) // Unvisited yet.
dfsCycle(tgtNode);

int tgtNodeOrder = visitedOrders[tgtNode];

// Target node is also still under DFS: a cycle just forms.
if (tgtNodeOrder > 0) {
// Such a cycle starts at target, and ends at current node.
int cycleLen = visitedOrders[node] + 1 - tgtNodeOrder;
if (cycleLen > maxCycle)
maxCycle = cycleLen;
}

visitedOrders[node] = -2; // Current node finalizes DFS.
}

public:
int computeLongestCycle(vector<int>& edges) {
graph = edges;

// Special marks: -1 = unvisited. -2 = has finalized DFS.
visitedOrders.assign(edges.size(), -1);

for (int node = 0; node < edges.size(); node++) {
if (visitedOrders[node] == -1) // Unvisited yet.
dfsCycle(node);
}

return maxCycle;
}
};

Follow-up Problem

If problem 2360 is clear, come take this.