How to sort depended objects by dependency

后端 未结 10 475
青春惊慌失措
青春惊慌失措 2020-11-28 19:31

I have a collection:

List> dependencyHierarchy;

The first item in pair is some object (item) and the

相关标签:
10条回答
  • 2020-11-28 20:06

    This is refactored code from post https://stackoverflow.com/a/9991916/4805491.

    // Version 1
    public static class TopologicalSorter<T> where T : class {
    
        public struct Item {
            public readonly T Object;
            public readonly T Dependency;
            public Item(T @object, T dependency) {
                Object = @object;
                Dependency = dependency;
            }
        }
    
    
        public static T[] Sort(T[] objects, Func<T, T, bool> isDependency) {
            return Sort( objects.ToList(), isDependency ).ToArray();
        }
    
        public static T[] Sort(T[] objects, Item[] dependencies) {
            return Sort( objects.ToList(), dependencies.ToList() ).ToArray();
        }
    
    
        private static List<T> Sort(List<T> objects, Func<T, T, bool> isDependency) {
            return Sort( objects, GetDependencies( objects, isDependency ) );
        }
    
        private static List<T> Sort(List<T> objects, List<Item> dependencies) {
            var result = new List<T>( objects.Count );
    
            while (objects.Any()) {
                var obj = GetIndependentObject( objects, dependencies );
                RemoveObject( obj, objects, dependencies );
                result.Add( obj );
            }
    
            return result;
        }
    
        private static List<Item> GetDependencies(List<T> objects, Func<T, T, bool> isDependency) {
            var dependencies = new List<Item>();
    
            for (var i = 0; i < objects.Count; i++) {
                var obj1 = objects[i];
                for (var j = i + 1; j < objects.Count; j++) {
                    var obj2 = objects[j];
                    if (isDependency( obj1, obj2 )) dependencies.Add( new Item( obj1, obj2 ) ); // obj2 is dependency of obj1
                    if (isDependency( obj2, obj1 )) dependencies.Add( new Item( obj2, obj1 ) ); // obj1 is dependency of obj2
                }
            }
    
            return dependencies;
        }
    
    
        private static T GetIndependentObject(List<T> objects, List<Item> dependencies) {
            foreach (var item in objects) {
                if (!GetDependencies( item, dependencies ).Any()) return item;
            }
            throw new Exception( "Circular reference found" );
        }
    
        private static IEnumerable<Item> GetDependencies(T obj, List<Item> dependencies) {
            return dependencies.Where( i => i.Object == obj );
        }
    
        private static void RemoveObject(T obj, List<T> objects, List<Item> dependencies) {
            objects.Remove( obj );
            dependencies.RemoveAll( i => i.Object == obj || i.Dependency == obj );
        }
    
    }
    
    
    // Version 2
    public class TopologicalSorter {
    
        public static T[] Sort<T>(T[] source, Func<T, T, bool> isDependency) {
            var list = new LinkedList<T>( source );
            var result = new List<T>();
    
            while (list.Any()) {
                var obj = GetIndependentObject( list, isDependency );
                list.Remove( obj );
                result.Add( obj );
            }
    
            return result.ToArray();
        }
    
        private static T GetIndependentObject<T>(IEnumerable<T> list, Func<T, T, bool> isDependency) {
            return list.First( i => !GetDependencies( i, list, isDependency ).Any() );
        }
    
        private static IEnumerable<T> GetDependencies<T>(T obj, IEnumerable<T> list, Func<T, T, bool> isDependency) {
            return list.Where( i => isDependency( obj, i ) ); // i is dependency of obj
        }
    
    }
    
    0 讨论(0)
  • 2020-11-28 20:11

    Perfect example to use a topological sort :

    http://en.wikipedia.org/wiki/Topological_sorting

    It will give you exactly what you need.

    0 讨论(0)
  • 2020-11-28 20:12

    This is my own re-implementation of Topological sorting, the idea is based on http://tawani.blogspot.com/2009/02/topological-sorting-and-cyclic.html (The ported Java source code consumes too much memory, checking 50k objects costs 50k*50k*4 = 10GB which is unacceptable. In addition, it still has Java coding convention some places)

    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.")
            {            
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-28 20:13

    I liked DMM's answer, but it assumes that the input nodes are leaves (which may or may not be what is expected).

    I am posting an alternate solution using LINQ that does not make this assumption. In addition, this solution uses yield return to be able to quickly return the leaves (using e.g. 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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题