这是寻找一组节点的共同祖先的基本思路:
- 对于每个选定的节点,获取包含自身及其祖先的节点集;
- 找到所有这些节点的交集;
- 其中,取深度最高的节点。
那么,我们该怎么做呢?
让我们首先编写一个属性来获取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