函数列表变量不返回递归变量的当前状态

本文关键字:变量 状态 返回 列表 函数 递归 | 更新日期: 2023-09-27 18:05:06

我正在使用一个图数据结构,并有一个递归函数,通过计算父节点到根节点的深度来计算节点的深度。

还有其他一些问题需要处理,但目前我的主要问题是存储递归字典参数的当前值,该参数存储路径分支。

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;
    }
}

当foreach循环遇到一个有多个父节点的节点时,它创建一个新的分支,应该用节点的当前id填充它。

C D ' / B | A | ...

会发生什么

使用上面的例子,从A开始,它将首先迭代B,作为它的直接父级。然后它从B的父节点开始,它有两个父节点,因此,它创建了一个单独的分支,并且应该用B和它的子节点填充该分支(直到开始节点,这次是a)。

不知何故,当B完成对C的迭代时,parent D轮询当前路径并返回B, C,实际上它应该只是B,因为C是兄弟,而不是直接子或父。

<标题>巨大编辑

我所附的代码完全开箱即用,并包含一个示例。您可以看到结果包含一些异常结果,例如

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

实际上应该是

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

函数列表变量不返回递归变量的当前状态

当您将字典作为方法的参数时,不会复制字典的内容,只复制对字典的引用。所以改变一个递归分支的字典也会改变另一个递归分支的字典。要解决这个问题,你可以在传递字典时自己显式地复制字典: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字典,并且这里唯一的输出是节点的"最大深度",您可能可以大大简化代码,例如:
private int getMaxNodeDepth(Node n)
{
     if (n.Dependencies == null || n.Dependencies.Count == 0) return 1;
     return 1 + n.Dependencies.Max(parent => getMaxNodeDepth(parent));
}

EDIT:已编辑以添加一个返回"最大路径"的解决方案:

    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);
    }