如何将树过滤到普通父级

时间:2016-03-02 00:45:09

标签: c# algorithm

我有一棵树(带复选框),如下所示。

A
    A1 
        A11 
            A111 (selected)
            A112 (selected)
            A113 
        B11
            B111 (selected)
            B112

我想过滤此项以返回如下,因为A1是所选节点的公共父级

A1
   A11
       A111
       A112
    B11
       B111

树是具有根节点及其子节点的层次结构:

public class Node
{
  public int Id;
  public string Name;
  public Node Parent;
  public List<Node> Children;
}

基本上,这是UI中的树结构。根据用户选择的内容(复选框),我必须找到公共父级并显示结果树。

2 个答案:

答案 0 :(得分:3)

这里是找到一组节点的共同祖先的基本思想:

  1. 对于每个选定的节点,获取包括其自身及其祖先的节点集;
  2. 找到所有这些节点的交集;
  3. 其中,请选择深度最高的节点。
  4. 那么,我们该怎么做呢?

    让我们首先编写一个属性来获取Node的祖先列表。 (我们需要&#34;和self&#34;部分来处理只选择一个节点的情况 - 在这种情况下,公共节点就是那个节点本身而我假设你没有想要父母。如果我错了并且你想让它找到父母,你可以改变这个属性只返回严格的祖先,但是你需要为根节点添加一个特殊情况,没有祖先。)

    public List<Node> AncestorsAndSelf
    {
        get
        {
            List<Node> list = new List<Node> { this };
            Node p = Parent;
            while (p != null)
            {
                list.Add(p);
                p = p.Parent;
            }
            return list;
        }
    }
    

    System.Linq命名空间中已有一个Intersect方法可以找到一组项目的交集,因此我们已经覆盖了第2步。

    对于第三步,我们需要一种方法来获取节点的深度。但是因为我们已经编写了AncestorsAndSelf属性,所以这很简单:

    public int Depth
    {
        get { return AncestorsAndSelf.Count; }
    }
    

    现在我们已经完成了所有部分,我们可以编写一个方法来查找所选节点集的共同祖先:

    public static Node FindClosestCommonAncestor(IEnumerable<Node> selectedNodes)
    {
        IEnumerable<Node> commonAncestors = selectedNodes.First().AncestorsAndSelf;
        foreach (Node n in selectedNodes.Skip(1))
        {
            commonAncestors = commonAncestors.Intersect(n.AncestorsAndSelf);
        }
        return commonAncestors.OrderByDescending(n => n.Depth).FirstOrDefault();
    }
    

    一旦我们拥有共同的祖先,我们需要一种打印子树的方法。这可以通过这样的简单递归方法完成:

    public override string ToString()
    {
        return ToString("");
    }
    
    private string ToString(string indent)
    {
        string s = indent + Name + "\r\n";
        foreach (Node child in Children)
        {
            s += child.ToString(indent + "    ");
        }
        return s;
    }
    

    以下是展示整个行动的演示:https://dotnetfiddle.net/cl7JGp

答案 1 :(得分:0)

假设您有一个名为nodes的根级节点列表,您可以这样做:

var node = nodes.SingleOrDefault(_ => _.Name == "A1");
if (null != node)
    return node.Children;
return new List<Node>(); 
// or you can return null if you'd prefer