I went ahead and implemented Aaron's algorithm in C #.
Since it uses IEnumerable, which is lazily enumerated, you can use DirectedGraphHelper.FindSimpleCycles (s) .First () if you want the first loop to be found:
public static class DirectedGraphHelper { public static IEnumerable<Node[]> FindSimpleCycles(Node startNode) { return FindSimpleCyclesCore(new Stack<Node>(new[] { startNode })); } private static IEnumerable<Node[]> FindSimpleCyclesCore(Stack<Node> pathSoFar) { var nodeJustAdded = pathSoFar.Peek(); foreach (var target in nodeJustAdded.Targets) { if (pathSoFar.Contains(target)) { yield return pathSoFar.Reverse().Concat(new[] { target }).ToArray(); } else { pathSoFar.Push(target); foreach (var simpleCycle in FindSimpleCyclesCore(pathSoFar)) { yield return simpleCycle; } pathSoFar.Pop(); } } } } public class Node { public string Id { get; private set; } public readonly List<Node> Targets = new List<Node>(); public Node(string id) { this.Id = id; } }
And you will use it as follows:
class Program { static void Main(string[] args) { var s = new Node("s"); var t = new Node("t"); var a = new Node("a"); var b = new Node("b"); var c = new Node("c"); var d = new Node("d"); s.Targets.Add(t); t.Targets.Add(a); a.Targets.AddRange(new[] { b, d }); b.Targets.Add(c); c.Targets.Add(a); d.Targets.Add(c); foreach (var cycle in DirectedGraphHelper.FindSimpleCycles(s)) { Console.WriteLine(string.Join(",", cycle.Select(n => n.Id))); } Console.Read(); } }
source share