使用LINQ搜索树

Ufu*_*arı 82 .net c# linq

我有一个从这个类创建的树.

class Node
{
    public string Key { get; }
    public List<Node> Children { get; }
}
Run Code Online (Sandbox Code Playgroud)

我想搜索所有孩子和他们所有的孩子,以获得符合条件的孩子:

node.Key == SomeSpecialKey
Run Code Online (Sandbox Code Playgroud)

我该如何实现它?

vid*_*ige 166

这是一种误解,这需要递归.这需要一个堆栈或队列和最简单的方法是使用递归来实现它.为了完整起见,我将提供一个非递归的答案.

static IEnumerable<Node> Descendants(this Node root)
{
    var nodes = new Stack<Node>(new[] {root});
    while (nodes.Any())
    {
        Node node = nodes.Pop();
        yield return node;
        foreach (var n in node.Children) nodes.Push(n);
    }
}
Run Code Online (Sandbox Code Playgroud)

例如,使用此表达式来使用它:

root.Descendants().Where(node => node.Key == SomeSpecialKey)
Run Code Online (Sandbox Code Playgroud)

  • +1.当树太深以至于递归遍历会破坏调用堆栈并导致`StackOverflowException`时,此方法将继续工作. (30认同)
  • 我认为值得一提的是,上面提出的解决方案执行(最后一个孩子优先)深度优先搜索.如果你想要一个(第一个孩子优先)广度优先搜索,你可以将节点集合的类型改为`Queue <Node>`(从'Push` /相应更改为'Enqueue` /`Dequeue` `Pop`). (11认同)
  • @LukeH虽然在这些情况下有这样的替代方案是有用的,但这意味着一棵非常大的树.除非你的树很深,否则递归方法通常更简单/更易读. (3认同)
  • @Tuskan:使用递归迭代器也有性能影响,请参阅http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx的"迭代器成本"部分(诚然,树木仍需要相当深,才能引人注目).并且,fwiw,我发现vidstige的答案与此处的递归答案一样可读. (3认同)
  • 是的,不要因为性能而选择我的解决方案.可读性始终是第一位的,除非证明是瓶颈.虽然我的解决方案非常直接,所以我想这是一个品味问题......我实际上只是作为递归答案的补充而发布我的答案,但我很高兴人们喜欢它. (3认同)
  • 我实际上没有意识到树和图之间的区别,不用担心这个答案。至于命名我不确定。我考虑过“SelectNodes”、“TraverseTree”和“Flatten”。实际上,我最终离开了它,并将“where”反转为“do ... where”以排除根节点,因为它适用于我的用例。 (2认同)

CD.*_*D.. 15

用Linq搜索对象树

public static class TreeToEnumerableEx
{
    public static IEnumerable<T> AsDepthFirstEnumerable<T>(this T head, Func<T, IEnumerable<T>> childrenFunc)
    {
        yield return head;

        foreach (var node in childrenFunc(head))
        {
            foreach (var child in AsDepthFirstEnumerable(node, childrenFunc))
            {
                yield return child;
            }
        }

    }

    public static IEnumerable<T> AsBreadthFirstEnumerable<T>(this T head, Func<T, IEnumerable<T>> childrenFunc)
    {
        yield return head;

        var last = head;
        foreach (var node in AsBreadthFirstEnumerable(head, childrenFunc))
        {
            foreach (var child in childrenFunc(node))
            {
                yield return child;
                last = child;
            }
            if (last.Equals(node)) yield break;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)


For*_*say 15

如果你想保持Linq之类的语法,你可以使用一个方法来获取所有的后代(儿童+孩子的孩子等)

static class NodeExtensions
{
    public static IEnumerable<Node> Descendants(this Node node)
    {
        return node.Children.Concat(node.Children.SelectMany(n => n.Descendants()));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后可以像使用where或first或者其他任何其他任何其他人一样查询该可枚举.