gar*_*rik 72 c# sorting algorithm dependencies topological-sort
我有一个集合:
List<VPair<Item, List<Item>> dependencyHierarchy;
Run Code Online (Sandbox Code Playgroud)
对中的第一项是某个对象(项),第二项是第一项依赖的相同类型对象的集合.我希望得到一个List<Item>依赖顺序,所以没有依赖于第一个元素的项目等等(没有循环依赖!).
输入:
Item4 depends on Item3 and Item5 Item3 depends on Item1 Item1 does not depend on any one Item2 depends on Item4 Item5 does not depend on any one
结果:
Item1 Item5 Item3 Item4 Item2
谢谢.
解:
拓扑排序(感谢LoïcFévrier的想法)
和
Mes*_*smo 83
经过一段时间的努力,这是我尝试使用Linq风格的TSort扩展方法:
public static IEnumerable<T> TSort<T>( this IEnumerable<T> source, Func<T, IEnumerable<T>> dependencies, bool throwOnCycle = false )
{
var sorted = new List<T>();
var visited = new HashSet<T>();
foreach( var item in source )
Visit( item, visited, sorted, dependencies, throwOnCycle );
return sorted;
}
private static void Visit<T>( T item, HashSet<T> visited, List<T> sorted, Func<T, IEnumerable<T>> dependencies, bool throwOnCycle )
{
if( !visited.Contains( item ) )
{
visited.Add( item );
foreach( var dep in dependencies( item ) )
Visit( dep, visited, sorted, dependencies, throwOnCycle );
sorted.Add( item );
}
else
{
if( throwOnCycle && !sorted.Contains( item ) )
throw new Exception( "Cyclic dependency found" );
}
}
Run Code Online (Sandbox Code Playgroud)
Loï*_*ier 41
使用拓扑排序的完美示例:
http://en.wikipedia.org/wiki/Topological_sorting
它将为您提供您所需要的.
sin*_*law 19
这有一个难题.
对于我们这些不想重新发明轮子的人:使用nuget安装QuickGraph .NET库,其中包括多种图形算法,包括拓扑排序.
要使用它,您需要创建一个AdjacencyGraph<,>这样的实例AdjacencyGraph<String, SEdge<String>>.然后,如果您包含适当的扩展名:
using QuickGraph.Algorithms;
Run Code Online (Sandbox Code Playgroud)
你可以打电话:
var sorted = myGraph.TopologicalSort();
Run Code Online (Sandbox Code Playgroud)
获取已排序节点的列表.
Kru*_*lur 11
我喜欢DMM的答案,但它假设输入节点是叶子(可能是也可能不是预期的).
我发布了一个使用LINQ的替代解决方案,但没有做出这个假设.此外,该解决方案用于yield return能够快速返回叶子(例如使用TakeWhile).
public static IEnumerable<T> TopologicalSort<T>(this IEnumerable<T> nodes,
Func<T, IEnumerable<T>> connected)
{
var elems = nodes.ToDictionary(node => node,
node => new HashSet<T>(connected(node)));
while (elems.Count > 0)
{
var elem = elems.FirstOrDefault(x => x.Value.Count == 0);
if (elem.Key == null)
{
throw new ArgumentException("Cyclic connections are not allowed");
}
elems.Remove(elem.Key);
foreach (var selem in elems)
{
selem.Value.Remove(elem.Key);
}
yield return elem.Key;
}
}
Run Code Online (Sandbox Code Playgroud)
这是我自己重新实现的拓扑排序,这个想法是基于http://tawani.blogspot.com/2009/02/topological-sorting-and-cyclic.html(移植的Java源代码消耗太多内存,检查50k对象的成本是50k*50k*4 = 10GB这是不可接受的.此外,它还有一些地方的Java编码约定)
using System.Collections.Generic;
using System.Diagnostics;
namespace Modules
{
/// <summary>
/// Provides fast-algorithm and low-memory usage to sort objects based on their dependencies.
/// </summary>
/// <remarks>
/// Definition: http://en.wikipedia.org/wiki/Topological_sorting
/// Source code credited to: http://tawani.blogspot.com/2009/02/topological-sorting-and-cyclic.html
/// Original Java source code: http://www.java2s.com/Code/Java/Collections-Data-Structure/Topologicalsorting.htm
/// </remarks>
/// <author>ThangTran</author>
/// <history>
/// 2012.03.21 - ThangTran: rewritten based on <see cref="TopologicalSorter"/>.
/// </history>
public class DependencySorter<T>
{
//**************************************************
//
// Private members
//
//**************************************************
#region Private members
/// <summary>
/// Gets the dependency matrix used by this instance.
/// </summary>
private readonly Dictionary<T, Dictionary<T, object>> _matrix = new Dictionary<T, Dictionary<T, object>>();
#endregion
//**************************************************
//
// Public methods
//
//**************************************************
#region Public methods
/// <summary>
/// Adds a list of objects that will be sorted.
/// </summary>
public void AddObjects(params T[] objects)
{
// --- Begin parameters checking code -----------------------------
Debug.Assert(objects != null);
Debug.Assert(objects.Length > 0);
// --- End parameters checking code -------------------------------
// add to matrix
foreach (T obj in objects)
{
// add to dictionary
_matrix.Add(obj, new Dictionary<T, object>());
}
}
/// <summary>
/// Sets dependencies of given object.
/// This means <paramref name="obj"/> depends on these <paramref name="dependsOnObjects"/> to run.
/// Please make sure objects given in the <paramref name="obj"/> and <paramref name="dependsOnObjects"/> are added first.
/// </summary>
public void SetDependencies(T obj, params T[] dependsOnObjects)
{
// --- Begin parameters checking code -----------------------------
Debug.Assert(dependsOnObjects != null);
// --- End parameters checking code -------------------------------
// set dependencies
Dictionary<T, object> dependencies = _matrix[obj];
dependencies.Clear();
// for each depended objects, add to dependencies
foreach (T dependsOnObject in dependsOnObjects)
{
dependencies.Add(dependsOnObject, null);
}
}
/// <summary>
/// Sorts objects based on this dependencies.
/// Note: because of the nature of algorithm and memory usage efficiency, this method can be used only one time.
/// </summary>
public T[] Sort()
{
// prepare result
List<T> result = new List<T>(_matrix.Count);
// while there are still object to get
while (_matrix.Count > 0)
{
// get an independent object
T independentObject;
if (!this.GetIndependentObject(out independentObject))
{
// circular dependency found
throw new CircularReferenceException();
}
// add to result
result.Add(independentObject);
// delete processed object
this.DeleteObject(independentObject);
}
// return result
return result.ToArray();
}
#endregion
//**************************************************
//
// Private methods
//
//**************************************************
#region Private methods
/// <summary>
/// Returns independent object or returns NULL if no independent object is found.
/// </summary>
private bool GetIndependentObject(out T result)
{
// for each object
foreach (KeyValuePair<T, Dictionary<T, object>> pair in _matrix)
{
// if the object contains any dependency
if (pair.Value.Count > 0)
{
// has dependency, skip it
continue;
}
// found
result = pair.Key;
return true;
}
// not found
result = default(T);
return false;
}
/// <summary>
/// Deletes given object from the matrix.
/// </summary>
private void DeleteObject(T obj)
{
// delete object from matrix
_matrix.Remove(obj);
// for each object, remove the dependency reference
foreach (KeyValuePair<T, Dictionary<T, object>> pair in _matrix)
{
// if current object depends on deleting object
pair.Value.Remove(obj);
}
}
#endregion
}
/// <summary>
/// Represents a circular reference exception when sorting dependency objects.
/// </summary>
public class CircularReferenceException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="CircularReferenceException"/> class.
/// </summary>
public CircularReferenceException()
: base("Circular reference found.")
{
}
}
}
Run Code Online (Sandbox Code Playgroud)