【问题标题】:Recursive collection search递归集合搜索
【发布时间】:2013-07-13 13:17:10
【问题描述】:

我有一个对象集合 (List<Element>),如下所述:

class Element
{
  string Name;
  string Value;
  ICollection<Element> ChildCollection;
  IDictionary<string, string> Attributes;
}

我基于我读入的一些 XML 构建了一个 Element 对象的 List&lt;Element&gt; 集合,我对此非常满意。目前如何实现对这些元素的搜索让我不解,但想知道是否有更好的解决方案。

集合的结构如下所示:

- Element (A)
  - Element (A1)
    - Element (A1.1)
  - Element (A2)
- Element (B)
  - Element (B1)
    - Element (B1.1)
    - Element (B1.2)
- Element (C)
  - Element (C1)
  - Element (C2)
  - Element (C3)

目前我正在使用递归搜索每个顶级(A、B、C)ElementAttributes 字典以查找特定的KeyValuePair。如果我在顶级 Element 中没有找到它,我开始以相同的方式搜索它的 ChildElement 集合(1、1.1、2、2.1、n 等)。

我很好奇的是是否有更好的方法来实现对这些对象的搜索,或者在这种情况下递归是否是更好的答案,如果我应该像目前一样实现搜索,top -> child -> child -> 等等,或者我是否应该以其他方式搜索,例如首先搜索所有顶级?

我是否可以使用 TPL 并行搜索每个顶层(A、B、C)并且是否合理?

【问题讨论】:

  • 你在搜索什么?

标签: c# search recursion task-parallel-library icollection


【解决方案1】:

递归是实现树形搜索的一种方式,您可以在其中以深度优先的顺序访问元素。您可以使用堆栈数据结构来存储您需要访问的树的节点,从而使用循环而不是递归来实现相同的算法。

如果您对队列而不是堆栈使用相同的算法,则搜索将以呼吸优先的顺序进行。

在这两种情况下,一般算法如下所示:

var nodes = ... // some collection of nodes
nodes.Add(root);
while (nodes.Count != 0) {
    var current = nodes.Remove ... // Take the current node from the collection.
    foreach (var child in current.ChildCollection) {
        nodes.Add(child);
    }
    // Process the current node
    if (current.Attributes ...) {
        ...
    }
}

请注意,该算法不是递归的:它使用nodes 的显式集合来保存搜索的当前状态,而递归实现使用调用堆栈来实现相同的目的。如果nodesStack&lt;Element&gt;,则搜索在depth-first order 中进行;如果nodesQueue&lt;Element&gt;,则搜索在breadth-first order 中进行。

【讨论】:

  • 这让我朝着我希望的方向前进。答案也很好解释,所以谢谢。
【解决方案2】:

我从某处抓到了这个位,它不是我的,但我无法提供指向它的链接。这个类为递归搜索展平了一个树视图,看起来它应该为你做同样的事情。

public static class SOExtension
{
    public static IEnumerable<TreeNode> FlattenTree(this TreeView tv)
    {
        return FlattenTree(tv.Nodes);
    }

    public static IEnumerable<TreeNode> FlattenTree(this TreeNodeCollection coll)
    {
        return coll.Cast<TreeNode>()
                    .Concat(coll.Cast<TreeNode>()
                                .SelectMany(x => FlattenTree(x.Nodes)));
    }
}

我找到了我从中获得的链接 - 它非常易于使用。看一看。 Is there a method for searching for TreeNode.Text field in TreeView.Nodes collection?

【讨论】:

    【解决方案3】:

    您可以重复使用专为以不同方式遍历而设计的现有组件,例如NETFx IEnumerable.Traverse Extension Method。它允许您首先深入或广度。它让您可以先遍历可枚举的树,深度或广度。

    获取扁平化可枚举目录的示例:

    IEnumerable<DirectoryInfo> directories = ... ;
    
    IEnumerable<DirectoryInfo> allDirsFlattened = directories.Traverse(TraverseKind.BreadthFirst, dir => dir.EnumerateDirectories());
    
    foreach (DirectoryInfo directoryInfo in allDirsFlattened)
    {
        ...
    }
    

    对于 BreadhFirst,它在内部使用 Queue<T>,对于 DepthFirst,它在内部使用 Stack<T>

    它不是并行遍历节点,除非遍历需要资源,否则不适合在此级别使用并行性。但这取决于上下文。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-02
      • 2014-01-10
      • 2019-03-12
      相关资源
      最近更新 更多