4

如何从以下示例中获取结果集。

public class Parent
{
    public string Id { get; set; }
    public List<Child> Children { get; set; }
}

public class Child : Parent
{
    public bool Isleaf { get; set; }
}
Child c1 = new Child();
c1.Id = "c1";
c1.Isleaf = false;

Child c2 = new Child();
c2.Id = "c2";
c2.Isleaf = true;

Child c11 = new Child();
c11.Id = "c11";
c11.Isleaf = true;

Child c12 = new Child();
c12.Id = "c12";
c12.Isleaf = false;


Child c121 = new Child();
c121.Id = "c121";
c121.Isleaf = true;

c12.Children = new List<Child>() { c121 };
c1.Children = new List<Child>() { c11, c12 };

Parent p = new Parent();
p.Id = "P1";
p.Children = new List<Child>() { c1, c2 };

从上面的集合中,我想获取所有叶子节点为 true 的子节点的列表,即 List leafNode=new List {c2,c11,c21};

4

6 回答 6

4

我不建议尝试使用 lambda 表达式来解决这个问题。递归方法可能适合:

void FindLeaves(Parent p, ICollection<Child> leaves)
{
    if (p.Children != null)
        foreach (var child in p.Children)
        {
            if (child.Isleaf)
                leaves.Add(child);
            FindLeaves(child, leaves);
        }
}

var leaves = new List<Child>();
FindLeaves(p, leaves);

如果叶子相同的节点可以出现在树中的多个位置,您可能需要添加一些逻辑来防止两次包含任何子节点。例如

if (child.IsLeaf && !leaves.Contains(child) 
    leaves.Add(child)

如果以下任何不常见的条件为真,则递归解决方案可能不适合:

  • 您的树中存在循环的机会(例如 ChildA -> ChildB -> ChildA)。(堆栈溢出,除非添加了避免循环的混乱逻辑)
  • 这棵树可能非常深。(堆栈溢出)
  • 树很大,性能绝对是最重要的。
于 2013-10-01T05:05:44.733 回答
1

此解决方案基于 Igby Largeman,但它使用堆栈并移除递归以防止堆栈溢出:

void FindLeaves(Parent p, ICollection<Child> leaves)
{
    if (p.Children != null) return;

    var toVisit = new Stack<Child>(p.Children());

    while (toVisit.Count > 0) {
        var current = toVisit.Pop(); 

        foreach (var child in current.Children)
        {    
            if (child.Isleaf)
                leaves.Add(child);
            else
                toVisit.Push(child);
        }
    }
}
于 2013-10-01T08:36:29.507 回答
0

对于初学者来说,我会将 IsLeaf 实现为

    public bool Isleaf
    {
        get
        {
            return Children.Any();
        }
    }

其次,我将有一个包含所有节点的第二个集合,以便轻松地跨树中的所有节点进行平面查询。您可以使另一个名为 RootNode... 的类具有属性 AllNodes。然后你可以做...

var leafNodes = rootNode.AllNodes.Where(a => a.Isleaf);

Telerik ASP.NET RadTreeNode 控件正是这样做的目的是让生活更轻松。

http://www.telerik.com/community/forums/aspnet-ajax/treeview/radtreeview-looping.aspx

于 2013-10-01T05:05:08.860 回答
0
public static class SearcTree
{
    public static IEnumerable<T> GetLeaf<T>(this T rootNode, Func<T, IEnumerable<T>> childrenFunc)
    {
        var childrens = childrenFunc(rootNode);
        var haschild = childrens != null && childrens.Any();
        if (!haschild)
            yield return rootNode;
        else
            foreach (var node in childrenFunc(rootNode))
            {
                foreach (var child in GetLeaf(node, childrenFunc))
                {
                    childrens = childrenFunc(child);
                    haschild = childrenFunc(child) != null && childrens.Any();
                    if (!haschild)
                        yield return child;
                }
            }
    }
}



  //Uses: 
     var allLeaf = p.GetLeaf(root => root.Children);
于 2013-10-01T07:55:20.720 回答
0

尝试这个 :

Get the children for each parent that has Isleaf = true;

var child1 = Parent.Children.FirstOrDefault(a => a.Isleaf);  // result is c2
var child2 = c12.Children.FirstOrDefault(a => a.Isleaf);     // result is c121
var child3 = c1.Children.FirstOrDefault(a => a.Isleaf);      // result is c11

List leafNode=new List {child1 ,child2 ,child3 };

仅当您具有此 Parent -> Children 结构时,这才有效。如果你添加更多的孩子,你需要有 foreach 循环。我这样做的原因是我不知道在将孩子添加到父母时要建立什么联系。否则,如果您的所有孩子都在父子列表属性中。你可以简单地有一个 foreach 循环。

于 2013-10-01T05:10:57.340 回答
0

我编写了一个基于Teudimundo解决方案的通用解决方案。这适用于对象包含自身集合的对象树。这是一个简单的类来演示。

public class Thing
{
    public IEnumerable<Thing> Children { get; set; }
    public bool IsLeaf => Children == null || !Children.Any();
}

因此,通过这个非常基本的设置,您可以看到它创建了一个Thing. 我有Children一个属性,IsLeaf,让我们知道这Thing是否是一片叶子。由此,我创建了一个扩展方法IEnumerable,允许您找到这种场景的所有叶子。

public static class EnumerableExtensions
{
    public static IEnumerable<TSource> Leaves<TSource>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> children, Func<TSource, bool> isLeaf)
    {
        var nodes = new Stack<TSource>(source);
        while (nodes.Any())
        {
            var current = nodes.Pop();
            if(isLeaf(current))
            {
                yield return current;
                continue;
            }
            foreach (var child in children(current))
            {
                if (isLeaf(child))
                {
                    yield return child;
                }
                else
                {
                    nodes.Push(child);
                }
            }
        }
    }
}

这很棒,因为它适用于 Linq 语句。因此,如果您有 的集合Thing,您可以轻松地遍历树以找到您的集合的所有叶子。这是调用此扩展方法的样子。

var leaves = things.Leaves(t => t.Children, t => t.IsLeaf);

things是一个IEnumerableThing最简单的方法是在其中制作一个new List<Thing>()Thing并将其分配给things. 参数Leaves是我们需要遍历树的两个属性。第一个是Children树的子节点。第二个是IsLeaf告诉我们这Thing是否是叶子的属性。此调用返回一个IEnumerable<Thing>仅包含来自 的叶子things。如果.ToList()需要,可以将其列入列表。我不认为它可以变得比这更简单!

于 2017-03-03T18:35:52.727 回答