2009-11-15 64 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