namespace Algorithms { using System; using System.Collections.Generic; using System.Linq; public class TopologicalSorter { private class Relations { public int Dependencies = 0; public HashSet Dependents = new HashSet(); } private Dictionary _map = new Dictionary(); public void Add(ValueType obj) { if (!_map.ContainsKey(obj)) _map.Add(obj, new Relations()); } public void Add(ValueType obj, ValueType dependency) { if (dependency.Equals(obj)) return; if (!_map.ContainsKey(dependency)) _map.Add(dependency, new Relations()); var dependents = _map[dependency].Dependents; if (!dependents.Contains(obj)) { dependents.Add(obj); if (!_map.ContainsKey(obj)) _map.Add(obj, new Relations()); ++_map[obj].Dependencies; } } public void Add(ValueType obj, IEnumerable dependencies) { foreach (var dependency in dependencies) Add(obj, dependency); } public void Add(ValueType obj, params ValueType[] dependencies) { Add(obj, dependencies as IEnumerable); } public Tuple, IEnumerable> Sort() { List sorted = new List(), cycled = new List(); var map = _map.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); sorted.AddRange(map.Where(kvp => kvp.Value.Dependencies == 0).Select(kvp => kvp.Key)); for (int idx = 0; idx < sorted.Count; ++idx) sorted.AddRange(map[sorted[idx]].Dependents.Where(k => --map[k].Dependencies == 0)); cycled.AddRange(map.Where(kvp => kvp.Value.Dependencies != 0).Select(kvp => kvp.Key)); return new Tuple, IEnumerable>(sorted, cycled); } public void Clear() { _map.Clear(); } } } /* Example usage with Task object */ namespace ExampleApplication { using Algorithms; using System; using System.Collections.Generic; using System.Linq; public class Task { public string Message; } class Program { static void Main(string[] args) { List tasks = new List { new Task{ Message = "A - depends on B and C" }, //0 new Task{ Message = "B - depends on none" }, //1 new Task{ Message = "C - depends on D and E" }, //2 new Task{ Message = "D - depends on none" }, //3 new Task{ Message = "E - depends on F, G and H" }, //4 new Task{ Message = "F - depends on I" }, //5 new Task{ Message = "G - depends on none" }, //6 new Task{ Message = "H - depends on none" }, //7 new Task{ Message = "I - depends on none" }, //8 }; TopologicalSorter resolver = new TopologicalSorter(); // now setting relations between them as described above resolver.Add(tasks[0], new[] { tasks[1], tasks[2] }); //resolver.Add(tasks[1]); // no need for this since the task was already mentioned as a dependency resolver.Add(tasks[2], new[] { tasks[3], tasks[4] }); //resolver.Add(tasks[3]); // no need for this since the task was already mentioned as a dependency resolver.Add(tasks[4], tasks[5], tasks[6], tasks[7]); resolver.Add(tasks[5], tasks[8]); //resolver.Add(tasks[6]); // no need for this since the task was already mentioned as a dependency //resolver.Add(tasks[7]); // no need for this since the task was already mentioned as a dependency //resolver.Add(tasks[3], tasks[0]); // uncomment this line to test cycled dependency var result = resolver.Sort(); var sorted = result.Item1; var cycled = result.Item2; if (!cycled.Any()) { foreach (var d in sorted) Console.WriteLine(d.Message); } else { Console.Write("Cycled dependencies detected: "); foreach (var d in cycled) Console.Write($"{d.Message[0]} "); Console.WriteLine(); } Console.WriteLine("exiting..."); } } }