Function list variable doesn't return the current state of a recursive variable

时间:2015-07-28 16:34:36

标签: c# recursion unity3d

I am working with a graph data structure and have a recursive function to calculate the depth of a node by counting the parents to the root node.

There are some other issues that I need to deal with, but for right now my main problem is to do with storing the current value of the recursive dictionary parameter, which stores the path branches.

using System;
using System.Collections.Generic;
using System.Linq;

public class Node {
    public string name;
    public int ID;
    public int maxDepth;
    public readonly List<Node> Dependencies = new List<Node>();
    public readonly List<Node> Children = new List<Node>();

    public bool isOrphan {
        get {
            return Dependencies.Count == 0;
        }
    }

    public bool isParent {
        get {
            return Children.Count != 0;
        }
    }
}

public class test {
    private static readonly List<Node> nodes = new List<Node>(); 
    public static void Main() {
        Node A = new Node() {
            name = "A",
            ID = 1
        };

        Node B = new Node() {
            name = "B",
            ID = 2
        };

        Node C = new Node() {
            name = "C",
            ID = 3
        };

        Node D = new Node() {
            name = "D",
            ID = 4
        };

        Node E = new Node() {
            name = "E",
            ID = 5
        };

        Node F = new Node() {
            name = "F",
            ID = 6
        };

        Node G = new Node() {
            name = "G",
            ID = 7
        };

        nodes.Add(A);
        nodes.Add(B);
        nodes.Add(C);
        nodes.Add(D);
        nodes.Add(E);
        nodes.Add(F);
        nodes.Add(G);

        A.Children.Add(B);
        A.Children.Add(G);
        B.Children.Add(C);
        B.Children.Add(D);
        C.Children.Add(D);
        D.Children.Add(E);
        E.Children.Add(F);

        B.Dependencies.Add(A);
        C.Dependencies.Add(B);
        D.Dependencies.Add(B);
        D.Dependencies.Add(C);
        E.Dependencies.Add(D);
        E.Dependencies.Add(G);
        F.Dependencies.Add(E);
        G.Dependencies.Add(A);

        foreach (Node n in nodes) {
            n.maxDepth = getMaxNodeDepth(n);
        }

        Console.ReadLine();
    }

    private static int getMaxNodeDepth(Node n, string listIndex = "base",
                    Dictionary<string, List<int>> paths = null) {
        bool firstIteration = false;

        if (paths == null) {
            firstIteration = true;
            listIndex = n.name.Replace(" ", "-");
            paths = new Dictionary<string, List<int>> {
                {listIndex, new List<int>(0)} 
            };
        }

        // Prevent the starting node from being added to the path
        if (!paths[listIndex].Contains(n.ID) && !firstIteration)
            paths[listIndex].Add(n.ID);

        // This variable should take the CURRENT path and store it; 
        // not the value after all the recursion has completed.
        // Right now, the current path is affected by the recursions, somehow...
        List<int> currentPath = new List<int>(paths[listIndex]);

        foreach (Node parent in n.Dependencies) {
            if (n.Dependencies.Count >= 2) {
                listIndex = parent.name;
                paths.Add(listIndex, currentPath);
            }
            getMaxNodeDepth(parent, listIndex, paths);
        }

        // Print out branches
        if (firstIteration) {
            string list = n.name + "\n";
            int listNumber = 1;
            foreach (List<int> iList in paths.Values) {
                list += string.Format("Branch#{0} -- ", paths.Keys.ElementAt(listNumber - 1));
                int total = 0;
                foreach (int i in iList) {
                    list += string.Format("{0}, ", nodes.First(x => x.ID == i).name);
                    total++;
                }
                listNumber++;
                list += string.Format(" -- ({0})\n", total);
            }
            Console.WriteLine(list);
        }

        // Order all paths by length, return the highest count
        // This is to be used to space out the hierarchy properly
        return paths.Values.OrderByDescending(path => path.Count).First().Count;
    }
}

When the foreach loop encounters a node with more than one parent, it creates a new branch and should populate it with the current IDs of the nodes.

C D \ / B | A | ...

What should happen

Using the above example, beginning with A, it will first iterate B, as its direct parent. Then it begins on B's parents, which it has two of and because of this, it creates a separate branch and should fill that branch with B and its children (until the starting node, this time being A).

What actually does

Somehow, when B has finished iterating over C, parent D polls the current path and is returned B, C, where it should actually be just B, as C is a sibling, not a direct child or parent.

Huge edit

The code I've attached runs completely out of the box and contains an example. You can see the result contains some anomalous results, such as

F Branch#G -- E, D, G, A, -- (4)

which should actually be

G Branch#G -- G, A, -- (2)

1 个答案:

答案 0 :(得分:1)

当您将字典作为方法的参数时,不会复制字典的内容,只会复制对字典的引用。 因此,在一个递归分支中更改字典也将更改另一个分支的字典。 要修复它,您可以在传递字典时自己显式复制字典: getMaxNodeDepth(parent, listIndex, new Dictionary<string, List<int>>(paths));

编辑:实际上这不够,因为它会将引用复制到内部列表而不是内部列表的内容,所以你需要一个更嵌套的克隆代码:

    private Dictionary<string, List<int>> clone(Dictionary<string, List<int>>  map)
    {
        Dictionary<string, List<int>> clone = new Dictionary<string,  List<int>>(map.Count);
        foreach (var pair in map)
        {
            clone[pair.Key] = new List<int>(pair.Value);
        }
        return clone;
    }

    //And then call it from your code:
    getMaxNodeDepth(parent, listIndex, clone(paths));

但是,假设您不需要为外部代码填写此paths字典,此处唯一的输出是&#34;最大深度&#34;对于节点,您可以大量简化代码,例如:

private int getMaxNodeDepth(Node n)
{
     if (n.Dependencies == null || n.Dependencies.Count == 0) return 1;
     return 1 + n.Dependencies.Max(parent => getMaxNodeDepth(parent));
}

编辑:编辑以添加一个返回&#34;最大路径&#34;以及:

    private List<Node> getMaxNodeDepth(Node n)
    {
        List<Node> path =
            n.GetSubFolders().Select(getMaxNodeDepth).OrderByDescending(p => p.Count).
            FirstOrDefault() ?? new List<Node>();
        path.Insert(0, n);
        return path;
    }

编辑:根据OP的评论,这是一个返回所有可用路径的解决方案:

    private static List<List<Node>> getAllPaths(Node n)
    {
        if (n.Dependencies == null || n.Dependencies.Count == 0)
            return new List<List<Node>> { new List<Node> { n }};
        List<List<Node>> allPaths = n.Dependencies.SelectMany(getAllPaths).ToList();
        allPaths.ForEach(path => path.Insert(0, n));
        return allPaths;
    }

    private static int getMaxDepth(Node n)
    {
        return getAllPaths(n).Max(p => p.Count);
    }