RosettaCodeData/Task/Topological-sort/C-sharp/topological-sort-1.cs

140 lines
4.6 KiB
C#

namespace Algorithms
{
using System;
using System.Collections.Generic;
using System.Linq;
public class TopologicalSorter<ValueType>
{
private class Relations
{
public int Dependencies = 0;
public HashSet<ValueType> Dependents = new HashSet<ValueType>();
}
private Dictionary<ValueType, Relations> _map = new Dictionary<ValueType, Relations>();
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<ValueType> dependencies)
{
foreach (var dependency in dependencies) Add(obj, dependency);
}
public void Add(ValueType obj, params ValueType[] dependencies)
{
Add(obj, dependencies as IEnumerable<ValueType>);
}
public Tuple<IEnumerable<ValueType>, IEnumerable<ValueType>> Sort()
{
List<ValueType> sorted = new List<ValueType>(), cycled = new List<ValueType>();
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<ValueType>, IEnumerable<ValueType>>(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<Task> tasks = new List<Task>
{
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<Task> resolver = new TopologicalSorter<Task>();
// 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...");
}
}
}