2009-11-15 105 views
0

中的类我有一个类(节点),其中有子节点的属性,它是查找列表

我有节点的列表(其中的每一个节点可能会或可能不会有一个节点类的List本身内的子节点列表)我需要能够在节点列表/子节点中找到节点。

我试过在列表上查找,但那只会搜索列表中的节点类而不是子节点列表。看着C5库和一些二叉树,但找不到合适的东西。有什么建议?

public class Node 
{ 
     public Guid Id { get; set; } 
     public DateTime Created { get; set; } 
     public List<Node> Nodes { get;set;} 
} 

功能(结果是最终结果)

private void GetRecersive(List<Node> list, ref List<Node> result) 
     { 
      foreach (Node item in list) 
      { 

       if (item.ParentId.Equals(Guid.Empty)) 
       { 
        result.Add(item); 
       } 
       else 
       { 
        result.ForEach(x => x.FindNodes(y => y.Id.Equals(item.ParentId)).FirstOrDefault().Nodes.Add(item)); 
       } 

       List<Node> nodes = GetNodesByParent(item.Id); 

       GetRecersive(nodes, ref result); 
      } 
     } 
+0

您不需要通过列表结果与ref关键字。 – empi 2009-11-15 21:55:14

回答

0

我们似乎都已经过于复杂这一切。我向我的一位同事展示了这个问题,他制作了完美的下图。

private void BuildUpChildren(List<Node> list) 
     { 
      foreach (Node item in list) 
      { 
       List<Node> nodes = GetNodesByParent(item.Id); 
       item.Nodes.AddRange(nodes); 
       BuildUpChildren(nodes); 
      } 
} 
1

你将不得不寻找这个递归(在节点列表搜索,然后在每个节点列表前一节点列表中的节点等等),并保留访问节点的列表(否则如果结构中存在循环,则永远不会结束)。你尝试过这样做吗?

4

正如empi所说,递归搜索在这里是理想的。如果你真的得到了一棵树,即有没有周期,它是如此简单:

public class Node 
{ 
    // Properties as before 

    public Node FindById(Guid id) 
    { 
     if (id == Id) 
     { 
      return this; 
     } 
     foreach (Node node in Nodes) 
     { 
      Node found = node.FindById(id); 
      if (found != null) 
      { 
       return found; 
      } 
     } 
     return null; // Not found in this subtree 
    } 
} 

否则,你就需要保持一组节点(例如HashSet<Node>)你已经访问过。循环使各种事物讨厌:)

可以使用LINQ改写foreach循环:

return Nodes.Select(x => x.FindById(id)).FirstOrDefault(); 

,但我不知道这是真的在这种特殊情况下的显式循环一样清晰(尽管我是一个庞大的LINQ粉丝)。

1

这是我会怎么做覆盖的节点列表,不管它有多深:

Node类:

public class Node 
{ 
    public Guid Id { get; set; } 
    public DateTime Created { get; set; } 
    public List<Node> Nodes { get; set; } 

    public Node() 
    { 
     this.Nodes = new List<Node>(); 
    } 

    public List<Node> FindNodes(Func<Node, bool> condition) 
    { 
     List<Node> resList = new List<Node>(); 

     if (this.Nodes != null && this.Nodes.Count > 0) 
     { 
      this.Nodes.ForEach(x => 
       { 
        resList.AddRange(x.FindNodes(condition)); 
        if (condition(x)) 
         resList.Add(x); 
       } 
      ); 
     } 

     return resList; 
    } 
} 

例如具有这个节点列表:

List<Node> nodeList = new List<Node>() 
{ 
    new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 01, 10), 
     Nodes = { 
      new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 01, 11) }, 
      new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 01, 12) } } }, 
    new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 02, 10), 
     Nodes = { 
      new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 02, 11), 
       Nodes = { 
        new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 11, 11) }, 
        new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 12, 12), 
         Nodes = { 
          new Node() { Id = Guid.NewGuid(), Created = new DateTime(2011, 11, 11) }, 
          new Node() { Id = Guid.NewGuid(), Created = new DateTime(2011, 12, 12) } } } } }, 
      new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 02, 12) } } }, 
    new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 03, 10), 
     Nodes = { 
      new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 03, 11) }, 
      new Node() { Id = Guid.NewGuid(), Created = new DateTime(2009, 03, 12) } } }, 
}; 

我能找到的所有子节点我想这样的:

List<Node> resList = new List<Node>(); 

nodeList.ForEach(x => 
    resList.AddRange(x.FindNodes(y => y.Created.Day == 12))); 

你可以看任何你想要的东西。在上面的例子中,我搜索了在任何月份和年份的第12天创建的节点。

+0

感谢您的例子和代码tolism7它的工作原理就像您所说(我可以停止将我的头撞到桌子上)。 有一个问题我找到了我想要的节点,然后我需要将当前节点添加为子节点项目。如果你的findnodes函数返回一个新的列表而不是所选节点的主列表中的引用(我已经用完整的代码更新了我的问题),那么这将是一个好办法。 – monkeylee 2009-11-15 21:37:06

+0

返回列表中的元素不是新节点对象,这些元素只是对找到的节点的引用,所以你应该可以在没有任何问题的情况下向它们添加chiled节点。 如果我在哪里,我会尝试Konstantin的(请参阅他的答案)解决方案,我将替换该行: result .ForEach(x => x.FindNodes(y => y.Id.Equals(item.ParentId))。FirstOrDefault().Nodes.Add(item)); with: result.SelectMany(n => n .AllNodes).First(n => n.Id.Equals(item.ParentId)).Nodes.Add(item); 并且看看它是如何工作的 PS你不需要第二个p上的ref关键字你的方法的aramenter。 – tolism7 2009-11-15 22:01:32

2

您可以添加flatterns你层次码和使用LINQ:

public class Node 
{ 
    // Properties 

    public IEnumerable<Node> AllNodes 
    { 
     get 
     { 
      yield return this; 

      foreach (var node in Nodes.SelectMany(n => n.AllNodes)) 
       yield return node; 
     } 
    } 
} 

,并使用LINQ到对象:

var matches = nodes.SelectMany(n => n.AllNodes).Where(n => n.Created < DateTime.Today); 
+0

我喜欢这个! (没有关于实用性或适合性作为解决方案的想法,我只是喜欢它实现的目标)。 – Murph 2009-11-15 21:43:06

+0

确实美丽! – tolism7 2009-11-15 21:54:13