/// <summary> /// This is the recursive version of Tarjan's strongly connected component algorithm /// this is useful for reference and for testing whether the iterative version is correct /// </summary> /// <typeparam name="T"></typeparam> /// <param name="index"></param> /// <param name="n"></param> /// <param name="infoMap"></param> /// <param name="stack"></param> /// <param name="sccs"></param> /// <param name="getFanouts"></param> private static void StronglyConnect <T>(ref int index, T n, Dictionary <T, SccInfo> infoMap, Stack <T> stack, List <List <T> > sccs, Func <T, IEnumerable <T> > getFanouts) { // preprocess - on first visit stack.Push(n); var info = new SccInfo(index: index, lowLink: index, onStack: true); infoMap[n] = info; index++; foreach (var next in getFanouts(n)) { SccInfo nextInfo; if (!infoMap.TryGetValue(next, out nextInfo)) { // next has not been visited: recurse into it StronglyConnect(ref index, next, infoMap, stack, sccs, getFanouts); info.LowLink = Math.Min(info.LowLink, infoMap[next].LowLink); } else if (nextInfo.OnStack) { // next is still on stack and hence in the current SCC info.LowLink = Math.Min(info.LowLink, nextInfo.Index); } } // post process - after all children have been processed if (info.Index == info.LowLink) { // n is still a root node, pop the stack and generate an SCC var cycle = new List <T>(); T next; do { next = stack.Pop(); infoMap[next].OnStack = false; cycle.Add(next); }while (((object)next) != ((object)n)); sccs.Add(cycle); } }
/// <summary> /// Iterative implementation for finding StronglyConnectedComponent /// This implements a recursion stack of activation records to get around stack overflow /// in each iteration in the while loop, the current activation record on top of stack "todo" /// is examined, and processed according to activation record's current state. it progressed in /// sequence: /// - first visit, if first child is not visited yet, push first child, set waitingForChild to true, /// else, incr activation record /// - if waitingForChild, process child's result, incr activation record /// else, if child is not visited yet, push child, set waitingForChild to true /// else, incr activation record /// ... /// - after processing the last child (or if there is no children at all), IsFinalVisit is true, then /// do the post processing, and pop the activation record /// </summary> /// <typeparam name="T">data type for node</typeparam> /// <param name="index">current index for Tarjan's algorithm</param> /// <param name="node">node to visit</param> /// <param name="infoMap">dictionary to keep track of node's info for Tarjan algoritm</param> /// <param name="stack">stack of nodes in Tarjan's algorithm</param> /// <param name="sccs">list of strongly connected components found</param> /// <param name="getFanouts">function to find fanouts of nodes in the graph</param> private static void StronglyConnectIterative <T>(ref int index, T node, Dictionary <T, SccInfo> infoMap, Stack <T> stack, List <List <T> > sccs, Func <T, IEnumerable <T> > getFanouts) { var todo = new Stack <ActivationRecord <T> >(); // activation records belong to stack todo: every entry in todo has one activation record // created on push, removed on pop todo.Push(AllocateActivationRecord(node, getFanouts)); while (todo.Any()) { SccInfo info; var currState = todo.Peek(); var curr = currState.Node; if (currState.IsFirstVisit()) { // preprocess - on first visit stack.Push(curr); info = new SccInfo(index: index, lowLink: index, onStack: true); infoMap[curr] = info; index++; } else { info = infoMap[curr]; } T child; if (currState.TryGetChild(out child)) { if (currState.WaitingForChild) { // returned from recursing into child previously info.LowLink = Math.Min(info.LowLink, infoMap[child].LowLink); // done with child currState.WaitingForChild = false; currState.MoveToNextChild(); } else { // first processing of child SccInfo childInfo; if (!infoMap.TryGetValue(child, out childInfo)) { //// child has not been visited: recurse into it //// StronglyConnect(ref index, next, infoMap, stack, sccs); //// info.LowLink = Math.Min(info.LowLink, nextInfo.LowLink); currState.WaitingForChild = true; todo.Push(AllocateActivationRecord(child, getFanouts)); } else { if (childInfo.OnStack) { // next is still on stack and hence in the current SCC info.LowLink = Math.Min(info.LowLink, childInfo.Index); } currState.MoveToNextChild(); } } } // allows post processing once the last child is processed if (currState.IsLastVisit()) { // all children have been processed : do post processing in recursion if (info.Index == info.LowLink) { // n is still a root node, pop the stack and generate an SCC var cycle = new List <T>(); T next; do { next = stack.Pop(); infoMap[next].OnStack = false; cycle.Add(next); }while (((object)next) != ((object)curr)); sccs.Add(cycle); } todo.Pop(); } } }