从下到上遍历任何深度的嵌套层次结构

时间:2019-04-09 17:42:12

标签: c# .net json recursion

采用这样的嵌套递归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处的子级开始),然后再回到根目录。我如何有效地解决这个问题?

1 个答案:

答案 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();

或其他。