采用这样的嵌套递归JSON代码段,该代码段可以继续进行到任何深度:
{
"Id": null,
"Foos": [
{
"FooId": 1,
"FooName": "ABC",
"Foos": [
{
"FooId": 2,
"FooName": "DEF",
"Foos": null
},
{
"FooId": 3,
"FooName": "GHI",
"Foos": [
{
"FooId": 4,
"FooName": "JKL",
"Foos": null
},
{
"FooId": 5,
"FooName": "MNO",
"Foos": [
{
"FooId": 6,
"FooName": "PQR",
"Foos": null
},
{
"FooId": 7,
"FooName": "STU",
"Foos": null
}
]
}
]
}
]
}
]
}
使用JSON.NET,我可以将其映射到这样的结构中:
public class Root {
public string Id { get; set; }
public List<Foo> Foos { get; set; }
}
public class Foo {
public int FooId { get; set; }
public string FooName { get; set; }
public List<Foo> Foos { get; set; }
}
到目前为止还不错...但是现在我需要从层次结构的底部开始工作(从FooId = 5处的子级开始),然后再回到根目录。我如何有效地解决这个问题?
答案 0 :(得分:3)
从您的问题中尚不清楚是要进行后置顺序(深度优先)遍历,还是要进行反向水平遍历(宽度优先,反向)。假设您想要后期订购,该算法非常简单:
public static IEnumerable<T> Postorder<T>(
this IEnumerable<T> nodes,
Func<T, IEnumerable<T>> children)
{
foreach(T node in nodes)
{
foreach(T descendant in children(node).Postorder(children))
yield return descendant;
yield return node;
}
}
每个节点仅在其所有后代之后才产生,因此这是一个后遍历。
如果树浅,那是相当有效的,但是您说您想解决“任何深度”的树的问题。 此方法仅对深度达几十个级别的树有效。,因为它是O(nd),其中n是节点总数,d是平均深度;平均深度取决于分支因子,因此可能低至1或高至n,这使其有可能成为四元算法。
此外,由于它使用O(dmax)堆栈空间,其中dmax是最大深度,因此我们可以删除调用堆栈。
因此:如果您有数百或数千个级别,请使用显式堆栈技术。
锻炼:重写我的算法以使用显式堆栈,而不是将调用堆栈用作隐式堆栈。
但是您说您需要任何深度的树木。如果树中有数十亿或数万亿个节点,深度达数十亿或数万亿怎么办?在这种情况下,您将需要使用外部存储器解决方案,并且我建议构建一个专门用于解决此问题的自定义存储系统。对大规模图形数据库进行了一些研究,可以解决此类问题。
无论如何,既然您已经有了通用的解决方案,那么您的特定解决方案将非常简单:
var ids = root.Foos
.Postorder(f => f.Foos)
.Select(f => f.FooId)
.ToList();
或其他。