简体   繁体   English

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

[英]Traversing a nested hierarchy of any depth from bottom - up

Take a nested recursive JSON snippet like this that can go on to any depth: 像这样嵌套嵌套的递归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
                        }
                     ]
                  }
               ]
            }
         ]
      }
   ]
}

Using JSON.NET I can map that into a structure like this: 使用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; }
}

so far so good...but now I need to work from the bottom of the hierarchy upward (starting with the children at FooId=5) and then working my way back up to the root. 到目前为止还不错...但是现在我需要从层次结构的底部开始向上工作(从FooId = 5处的子级开始),然后再回到根目录。 How do I tackle this efficiently? 我如何有效地解决这个问题?

It's unclear from your question whether you want a postorder (depth-first) traversal, or a reverse level traversal (breadth-first, reversed). 从您的问题尚不清楚,您是否需要后序(深度优先)遍历,还是反向级别遍历(宽度优先,反向)。 Assuming you want postorder, the algorithm is straightforward: 假设您想要后期订购,该算法非常简单:

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

Every node is yielded only after all of its descendants, so this is a postorder traversal. 每个节点仅在其所有后代之后才产生,因此这是一个后序遍历。

That's reasonably efficient if the tree is shallow, but you say you wish to solve the problem for a tree of "any depth". 如果树很浅,那是相当有效的,但是您说您希望解决“任何深度”的树的问题。 This approach will only work efficiently for trees of depths up to a few dozen levels because it is O(nd) where n is the total number of nodes and d is the average depth; 这种方法仅对深度达几十个级别的树有效,因为它是O(nd),其中n是节点总数,d是平均深度。 the average depth depends on the branching factor, and so could be as low as 1 or as high as n, making this a potentially quadradic algorithm. 平均深度取决于分支因子,因此可能低至1或高至n,这使其成为潜在的四元算法。

Moreover, since it uses O(dmax) stack space where dmax is the maximum depth, we can blow the call stack. 此外,由于它使用O(dmax)堆栈空间,其中dmax是最大深度,因此我们可以删除调用堆栈。

Thus: if you have hundreds or thousands of levels, use the explicit stack technique. 因此:如果您有数百或数千个级别,请使用显式堆栈技术。

Exercise : Rewrite my algorithm to use an explicit stack rather than using the call stack as an implicit stack. 练习 :重写我的算法以使用显式堆栈,而不是将调用堆栈用作隐式堆栈。

But you said you need trees of any depth . 但是你说你需要任何深度的树木。 What if there are billions, or trillions of nodes in the tree, billions or trillions deep? 如果树中有数十亿或数万亿个节点,深度达数十亿或数万亿怎么办? In that case you'll need to go with an external memory solution, and I would recommend building a custom storage system dedicated to this problem; 在这种情况下,您将需要使用外部存储器解决方案,并且我建议构建一个专门用于解决此问题的自定义存储系统。 do some research on at-scale graph databases, which can solve this sort of problem. 对大规模图形数据库进行一些研究,可以解决此类问题。

Anyways, now that you have the general solution, your specific solution is straightforward: 无论如何,既然您已经有了通用的解决方案,那么您的特定解决方案将非常简单:

var ids = root.Foos
              .Postorder(f => f.Foos)
              .Select(f => f.FooId)
              .ToList();

or whatever. 管他呢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM