Converting Directed Acyclic Graph (DAG) to tree

自作多情 提交于 2019-11-28 21:37:24

Algorithm:

  • As you observed, some nodes appear twice in the output. If the node 2 had children, the whole subtree would appear twice. If you want each node to appear just once, replace

    if (hashSet.Contains(node))
    {
        var nodeClone = new Node<T>(node.Value, node.Childs);
        node = nodeClone;
    }
    

    with

    if (hashSet.Contains(node))
    {
        // node already seen -> do nothing
    }
    
  • I wouldn't be too worried about the size of the stack or performance of recursion. However, you could replace your Depth-first-search with Breadth-first-search which would result in nodes closer to the root being visited earlier, thus yielding a more "natural" tree (in your picture you already numbered the nodes in BFS order).

     var seenNodes = new HashSet<Node>();
     var q = new Queue<Node>();
     q.Enqueue(root);
     seenNodes.Add(root);   
    
     while (q.Count > 0) {
         var node = q.Dequeue();
         foreach (var child in node.Childs) {
             if (!seenNodes.Contains(child )) {
                 seenNodes.Add(child);
                 q.Enqueue(child);
         }
     }
    

    The algorithm handles diamonds and cycles.

  • Multiple roots

    Just declare a class Graph which will contain all the vertices

    class Graph
    {
        public List<Node> Nodes { get; private set; }
        public Graph()
        {
            Nodes = new List<Node>();
        }    
    }
    

Code:

  • the hashSet could be named seenNodes.

  • Instead of

    var length = root.Childs.Count;
    for (int i = 0; i < length; ++i)
    {
        var node = root.Childs[i];
    

    write

    foreach (var child in root.Childs)
    
  • In Traverse, the visitor is quite unnecessary. You could rather have a method which yields all the nodes of the tree (in the same order traverse does) and it is up to user to do whatever with the nodes:

    foreach(var node in root.TraverseRecursive())
    {
        Console.WriteLine(node.Value);
    }
    
  • If you override GetHashCode and Equals, the algorithm will no more be able to distinguish between two different Nodes with same value, which is probably not what you want.

  • I don't see any reason why LinkedList would be better here than List, except for the reallocations (Capacity 2,4,8,16,...) which List does when adding nodes.

  1. you had better posted in CodeReview
  2. Childs is wrong => Children
  3. you don't have to use a HashSet, you could have easily used a List>, because checking references only is enough here. (and so no GetHashCode, Equals and operators overriding is needed)

  4. easeier way is Serializing your class and then Deserializing it again into second objectwith XmlSerializer. while Serialized and Deserialized, 1 object referenced 2 times will become 2 objects with different references.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!