函数列表变量不返回递归变量的当前状态
本文关键字:变量 状态 返回 列表 函数 递归 | 更新日期: 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);
}