Implement DFS (recursive and iterative) with topo sort, cycle detection, and timestamps
You are given a directed graph G with N nodes labeled 1..N and M edges (u, v).
Implement two versions of depth-first search (DFS) in Python:
-
Recursive DFS
-
Iterative DFS using your own explicit stack (must not rely on increasing Python's recursion limit)
Both versions must:
-
Return a topological ordering if G is a DAG; otherwise, detect and return any one directed cycle as an ordered list of nodes.
-
Compute discovery and finish timestamps for every node. Timestamps start at 1 and increment by 1 on each vertex discovery and each vertex finish (standard CLRS DFS timing).
-
Work correctly for disconnected graphs and graphs with self-loops and parallel edges.
-
Run in O(N+M) time and O(N+M) space.
Input to each function:
-
N (int)
-
edges (list of (u, v) pairs)
You should build an adjacency list internally.
Output from each function:
-
a dict with keys: {"is_dag": bool, "topo_order": list|None, "cycle": list|None, "discover": dict, "finish": dict}
Notes and edge cases:
-
Parallel edges between the same nodes must be handled correctly.
-
A self-loop (v, v) should be reported as a cycle [v, v].
-
Multiple valid topological orders or cycles are acceptable.
-
For N up to 100,000, the iterative version must avoid recursion depth issues.
Examples:
-
N=4, edges=[(1,2),(2,3),(3,4)] => is_dag=True, one valid topo_order=[1,2,3,4], cycle=None.
-
N=4, edges=[(1,2),(2,3),(3,2),(3,4)] => is_dag=False, cycle could be [2,3,2] or [3,2,3].
Also explain your design choices (e.g., visited color scheme, parent tracking for cycle reconstruction) and provide unit tests for both dense and sparse graphs.