Stable topological sort

前端 未结 6 980
Happy的楠姐
Happy的楠姐 2020-12-29 04:58

Let say I have a graph where the nodes is stored in a sorted list. I now want to topological sort this graph while keeping the original order where the topological order is

6条回答
  •  执念已碎
    2020-12-29 05:11

    The problem is two-fold:

    • Topological sort
    • Stable sort

    After many errors and trials I came up with a simple algorithm that resembles bubble sort but with topological order criteria.

    I thoroughly tested the algorithm on full graphs with complete edge combinations so it can be considered as proven.

    Cyclic dependencies are tolerated and resolved according to original order of elements in sequence. The resulting order is perfect and represents the closest possible match.

    Here is the source code in C#:

    static class TopologicalSort
    {
        /// 
        /// Delegate definition for dependency function.
        /// 
        /// The type.
        /// The A.
        /// The B.
        /// 
        /// Returns true when A depends on B. Otherwise, false.
        /// 
        public delegate bool TopologicalDependencyFunction(T a, T b);
    
        /// 
        /// Sorts the elements of a sequence in dependency order according to comparison function with Gapotchenko algorithm.
        /// The sort is stable. Cyclic dependencies are tolerated and resolved according to original order of elements in sequence.
        /// 
        /// The type of the elements of source.
        /// A sequence of values to order.
        /// The dependency function.
        /// The equality comparer.
        /// The ordered sequence.
        public static IEnumerable StableOrder(
            IEnumerable source,
            TopologicalDependencyFunction dependencyFunction,
            IEqualityComparer equalityComparer)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (dependencyFunction == null)
                throw new ArgumentNullException("dependencyFunction");
            if (equalityComparer == null)
                throw new ArgumentNullException("equalityComparer");
    
            var graph = DependencyGraph.TryCreate(source, dependencyFunction, equalityComparer);
            if (graph == null)
                return source;
    
            var list = source.ToList();
            int n = list.Count;
    
        Restart:
            for (int i = 0; i < n; ++i)
            {
                for (int j = 0; j < i; ++j)
                {
                    if (graph.DoesXHaveDirectDependencyOnY(list[j], list[i]))
                    {
                        bool jOnI = graph.DoesXHaveTransientDependencyOnY(list[j], list[i]);
                        bool iOnJ = graph.DoesXHaveTransientDependencyOnY(list[i], list[j]);
    
                        bool circularDependency = jOnI && iOnJ;
    
                        if (!circularDependency)
                        {
                            var t = list[i];
                            list.RemoveAt(i);
    
                            list.Insert(j, t);
                            goto Restart;
                        }
                    }
                }
            }
    
            return list;
        }
    
        /// 
        /// Sorts the elements of a sequence in dependency order according to comparison function with Gapotchenko algorithm.
        /// The sort is stable. Cyclic dependencies are tolerated and resolved according to original order of elements in sequence.
        /// 
        /// The type of the elements of source.
        /// A sequence of values to order.
        /// The dependency function.
        /// The ordered sequence.
        public static IEnumerable StableOrder(
            IEnumerable source,
            TopologicalDependencyFunction dependencyFunction)
        {
            return StableOrder(source, dependencyFunction, EqualityComparer.Default);
        }
    
        sealed class DependencyGraph
        {
            private DependencyGraph()
            {
            }
    
            public IEqualityComparer EqualityComparer
            {
                get;
                private set;
            }
    
            public sealed class Node
            {
                public int Position
                {
                    get;
                    set;
                }
    
                List _Children = new List();
    
                public IList Children
                {
                    get
                    {
                        return _Children;
                    }
                }
            }
    
            public IDictionary Nodes
            {
                get;
                private set;
            }
    
            public static DependencyGraph TryCreate(
                IEnumerable source,
                TopologicalDependencyFunction dependencyFunction,
                IEqualityComparer equalityComparer)
            {
                var list = source as IList;
                if (list == null)
                    list = source.ToArray();
    
                int n = list.Count;
                if (n < 2)
                    return null;
    
                var graph = new DependencyGraph();
                graph.EqualityComparer = equalityComparer;
                graph.Nodes = new Dictionary(n, equalityComparer);
    
                bool hasDependencies = false;
    
                for (int position = 0; position < n; ++position)
                {
                    var element = list[position];
    
                    Node node;
                    if (!graph.Nodes.TryGetValue(element, out node))
                    {
                        node = new Node();
                        node.Position = position;
                        graph.Nodes.Add(element, node);
                    }
    
                    foreach (var anotherElement in list)
                    {
                        if (equalityComparer.Equals(element, anotherElement))
                            continue;
    
                        if (dependencyFunction(element, anotherElement))
                        {
                            node.Children.Add(anotherElement);
                            hasDependencies = true;
                        }
                    }
                }
    
                if (!hasDependencies)
                    return null;
    
                return graph;
            }
    
            public bool DoesXHaveDirectDependencyOnY(T x, T y)
            {
                Node node;
                if (Nodes.TryGetValue(x, out node))
                {
                    if (node.Children.Contains(y, EqualityComparer))
                        return true;
                }
                return false;
            }
    
            sealed class DependencyTraverser
            {
                public DependencyTraverser(DependencyGraph graph)
                {
                    _Graph = graph;
                    _VisitedNodes = new HashSet(graph.EqualityComparer);
                }
    
                DependencyGraph _Graph;
                HashSet _VisitedNodes;
    
                public bool DoesXHaveTransientDependencyOnY(T x, T y)
                {
                    if (!_VisitedNodes.Add(x))
                        return false;
    
                    Node node;
                    if (_Graph.Nodes.TryGetValue(x, out node))
                    {
                        if (node.Children.Contains(y, _Graph.EqualityComparer))
                            return true;
    
                        foreach (var i in node.Children)
                        {
                            if (DoesXHaveTransientDependencyOnY(i, y))
                                return true;
                        }
                    }
    
                    return false;
                }
            }
    
            public bool DoesXHaveTransientDependencyOnY(T x, T y)
            {
                var traverser = new DependencyTraverser(this);
                return traverser.DoesXHaveTransientDependencyOnY(x, y);
            }
        }
    }
    

    And a small sample application:

    class Program
    {
        static bool DependencyFunction(char a, char b)
        {
            switch (a + " depends on " + b)
            {
                case "A depends on B":
                    return true;
    
                case "B depends on D":
                    return true;
    
                default:
                    return false;
            }
    
        }
    
        static void Main(string[] args)
        {
            var source = "ABCDEF";
            var result = TopologicalSort.StableOrder(source.ToCharArray(), DependencyFunction);
            Console.WriteLine(string.Concat(result));
        }
    }
    

    Given the input elements {A, B, C, D, E, F} where A depends on B and B depends on D the output is {D, B, A, C, E, F}.

    UPDATE: I wrote a small article about stable topological sort objective, algorithm and its proofing. Hope this gives more explanations and is useful to developers and researchers.

提交回复
热议问题