【问题标题】:How to filter tree to common parent如何将树过滤到共同父级
【发布时间】:2016-03-02 14:01:31
【问题描述】:

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

一个 A1 A11 A111(已选) A112(已选) A113 B11 B111(已选) 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 中的树形结构。根据用户选择的内容(复选框),我必须找到共同的父级并显示 resultant 树。

【问题讨论】:

  • 从发布的树层次结构/节点类中,子节点没有对父节点的引用。如果是这种情况,那么您将不得不递归搜索父节点和所有子节点以找到选定的节点,假设节点上有一些字段/属性将其标识为选中;可能会识别所有选定节点的分支深度,然后使用最高深度 - 1 找到父节点。
  • @MetroSmurf 请看看我编辑的课程。
  • “共同父母”的指标是什么?对我来说,应该是“A”,因为它们都属于 A——你是怎么来到“A1”的
  • 是直系共同父母

标签: c# algorithm


【解决方案1】:

这是寻找一组节点的共同祖先的基本思路:

  1. 对于每个选定的节点,获取包含自身及其祖先的节点集;
  2. 找到所有这些节点的交集;
  3. 其中,取深度最高的节点。

那么,我们该怎么做呢?

让我们首先编写一个属性来获取Node 的祖先列表。 (我们需要“和自我”部分来处理只选择一个节点的情况——在这种情况下,公共节点就是那个节点本身,我假设你不想要父节点。如果我错了并且您希望它找到父节点,您可以将此属性更改为仅返回严格的祖先,但是您需要为没有祖先的根节点添加一个特殊情况。)

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

【讨论】:

    【解决方案2】:

    假设您有一个名为 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
    

    【讨论】:

    • 这行不通,我必须根据在叶级别选择的内容找到common 父节点。它不是基于名称“A1”(“A1”只是一个示例名称,我什至不知道“A1”是公共父级,我必须根据用户选择的内容“找到”它。
    • 我错过了您问题的“选定”部分。我当时认为这是一个非常容易有人问的问题。有一篇旧的 Joel on Software 博客文章介绍了树结构以及他们如何管理任务树并保持它们的性能。我会看看能不能找到它,因为它是一个非常优雅的解决方案,它使用您可能会觉得有用的节点 ID。
    猜你喜欢
    • 1970-01-01
    • 2018-10-10
    • 1970-01-01
    • 1970-01-01
    • 2015-03-19
    • 2016-12-22
    • 1970-01-01
    • 1970-01-01
    • 2021-07-26
    相关资源
    最近更新 更多