1

I have a list of nodes like this

{ a, b , c }

with the dependencies between them defined as

  • a => { b, c }
  • b => { c }
  • c => { }

The algorithm should return a sorted list so any given node should appear before any of its dependencies For instance, a valid solution would be: {c, b, a}

So far I have this class:

public static class DependencySorter
{
    public static ICollection<T> SortDependencies<T>(this IEnumerable<T> nodes) where T : IDependency<T>
    {
        var set = new HashSet<T>();

        foreach (var node in nodes)
        {
            foreach (var dependency in node.Resolve())
            {
                set.Add(dependency);
            }                
        }

        return set.ToList();
    }

    public static ICollection<T> Resolve<T>(this T node) where T : IDependency<T>
    {
        var resolved = new Collection<T>();
        ResolveDependenciesRecursive(node, resolved, new List<T>());
        return resolved;
    }

    private static void ResolveDependenciesRecursive<T>(T node, ICollection<T> resolved, ICollection<T> notResolved) where T : IDependency<T>
    {
        notResolved.Add(node);
        foreach (var edge in node.Dependencies.Where(edge => !resolved.Contains(edge)))
        {
            if (notResolved.Contains(edge))
            {
                throw new InvalidOperationException($"Circular dependency detected {node} => {edge}");
            }

            ResolveDependenciesRecursive(edge, resolved, notResolved);
        }

        resolved.Add(node);
        notResolved.Remove(node);
    }
}

public interface IDependency<out T>
{
    IEnumerable<T> Dependencies { get; }
}

I'm sure that its performance and complexity is really bad.

6
  • 1
    And what have you tried so far? Also this problem might not be terribly straightforward if you need to consider things such as circular dependencies. Commented Sep 23, 2015 at 22:07
  • ... and which language? Commented Sep 23, 2015 at 22:08
  • @CollinD check the updated post! Commented Sep 23, 2015 at 22:09
  • 1
    Typical application of topological sorting. Commented Sep 23, 2015 at 22:10
  • Look at Breadth-first algorithm. Commented Sep 23, 2015 at 22:16

1 Answer 1

5

This is called "topological sorting". There are some efficient and relatively simple algorithms available (Wikipedia lists some), typically in O(|V|+|E|) time. My favorite is the one based on depth-first search:

L ← Empty list that will contain the sorted nodes
while there are unmarked nodes do
    select an unmarked node n
    visit(n) 

function visit(node n)
    if n has a temporary mark then stop (not a DAG)
    if n is not marked (i.e. has not been visited yet) then
        mark n temporarily
        for each node m with an edge from n to m do
            visit(m)
        mark n permanently
        unmark n temporarily
        add n to head of L

(This is a copy & paste from https://en.wikipedia.org/wiki/Topological_sorting)

Sign up to request clarification or add additional context in comments.

5 Comments

Could you please translate it to C# or Java :) Thanks!
@SuperJMN: Why don't you take your best stab at translating it. If you get stuck, ask a new question.
Thank you. I was in a hurry, so I wanted a ready-to-use version. Thank you!
What if the dependency graph is cyclic?
Then the problem is NP-complete. It's perfectly fine. Node.js does, so you can do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.