I have a collection:
List> dependencyHierarchy;
The first item in pair is some object (item) and the
I don't like recursive methods so DMM is out. Krumelur looks good but seems to use a lot of memory? Made an alternative stack based method that seems to work. Uses same DFS logic as DMM 's and I used this solutions as comparison when testing.
public static IEnumerable TopogicalSequenceDFS(this IEnumerable source, Func> deps)
{
HashSet yielded = new HashSet();
HashSet visited = new HashSet();
Stack>> stack = new Stack>>();
foreach (T t in source)
{
stack.Clear();
if (visited.Add(t))
stack.Push(new Tuple>(t, deps(t).GetEnumerator()));
while (stack.Count > 0)
{
var p = stack.Peek();
bool depPushed = false;
while (p.Item2.MoveNext())
{
var curr = p.Item2.Current;
if (visited.Add(curr))
{
stack.Push(new Tuple>(curr, deps(curr).GetEnumerator()));
depPushed = true;
break;
}
else if (!yielded.Contains(curr))
throw new Exception("cycle");
}
if (!depPushed)
{
p = stack.Pop();
if (!yielded.Add(p.Item1))
throw new Exception("bug");
yield return p.Item1;
}
}
}
}
Here is also a simpler stack based BFS variant. It will produce different result than the above, but still valid. I'm not sure if there is any advantage to using the DFS variant above, but it was fun creating it.
public static IEnumerable TopologicalSequenceBFS(this IEnumerable source, Func> dependencies)
{
var yielded = new HashSet();
var visited = new HashSet();
var stack = new Stack>(source.Select(s => new Tuple(s, false))); // bool signals Add to sorted
while (stack.Count > 0)
{
var item = stack.Pop();
if (!item.Item2)
{
if (visited.Add(item.Item1))
{
stack.Push(new Tuple(item.Item1, true)); // To be added after processing the dependencies
foreach (var dep in dependencies(item.Item1))
stack.Push(new Tuple(dep, false));
}
else if (!yielded.Contains(item.Item1))
throw new Exception("cyclic");
}
else
{
if (!yielded.Add(item.Item1))
throw new Exception("bug");
yield return item.Item1;
}
}
}
For .NET 4.7+ I suggest replacing Tuple with ValueTuple for lower memory use. In older .NET versions Tuple can be replaced with KeyValuePair.