python最低共同祖先

python最低共同祖先,python,algorithm,tree,Python,Algorithm,Tree,在Python中实现最低公共祖先的最简单方法是什么?我有一棵树,它由每个节点表示,每个节点都有一个指向其父节点的指针,我希望能够找到给定两个节点的第一个公共祖先。我想出了几个主意,但没有一个特别吸引人 让每个节点包含其基的列表,若要执行联接,请查找最长的公共前缀,然后获取最后一个元素。不幸的是,我不知道任何内置的方法来做最长的公共前缀,所以这需要手动循环 让每个节点包含一组基,执行一组交集,并取最大元素。但这需要定义自定义比较运算符,我甚至不确定它是否有效 我该怎么办?我正在寻找一种简单性优于性

在Python中实现最低公共祖先的最简单方法是什么?我有一棵树,它由每个节点表示,每个节点都有一个指向其父节点的指针,我希望能够找到给定两个节点的第一个公共祖先。我想出了几个主意,但没有一个特别吸引人

  • 让每个节点包含其基的列表,若要执行联接,请查找最长的公共前缀,然后获取最后一个元素。不幸的是,我不知道任何内置的方法来做最长的公共前缀,所以这需要手动循环

  • 让每个节点包含一组基,执行一组交集,并取最大元素。但这需要定义自定义比较运算符,我甚至不确定它是否有效

  • 我该怎么办?我正在寻找一种简单性优于性能的解决方案,因此需要复杂处理的解决方案已经过时

    编辑:我发现虽然没有内置的方法,但可以使用zip在一行中生成最长的公共前缀,所以它仍然相当简单

    common = [x for x in zip(*baselists) if len(set(x)) == 1][-1]
    

    获取每个节点的深度(与根的距离)。如果一个比另一个低,则从较低的节点向上爬树,直到深度相等。然后检查身份,每次检查失败时,在每一侧向上移动

    您可以通过一个while循环来实现这一点。一旦你选择了同样深度的祖先:

    while (ancestorA !== ancestorB) {
        ancestorA = ancestorA.parent();
        ancestorB = ancestorB.parent();
    }
    
    当while循环终止时,
    antestora
    antestorb
    将分别成为您的共同祖先


    这不仅应该非常简单,而且应该相当快

    假设无法修改树以包含深度,则可以执行以下操作:


    对于每个节点,递归地向上遍历树,直到到达根。在每个父节点上,将节点插入到
    列表中
    。这将为您提供
    列表a
    列表b
    。迭代最短的列表,比较每个列表中的元素。当您找到一个不匹配的元素时,上一个条目就是最大的父元素

    我想这取决于你的树,以及它将包含多少对象。如果这个数字在内存方面是合理的(可能少于几百万个节点,但这只是我的猜测),我会使用你的建议#2。在集合中,只保留每个基的字符串表示形式,这样内置的比较就可以工作了。应该非常快,我想你可以用几行代码来实现它。如果字符串表示法不实用,或者如果您需要对象本身,并且无法实现所有对象的主dict,那么只需在节点对象中定义比较方法(eqneq,如果我记得的话)。

    如果要维护父集合,最好使用带=,因为在这种情况下,您不会花费logn在父列表中搜索。所以,在每次迭代中检查这个映射,如果当前节点的父节点已经存在于映射中,那么这个父节点就是您的结果。在最坏的情况下,它给出O(n),但如果您在某些情况下同时开始分析两个节点,您将能够更快地找到它。

    既然您在这两种情况下都已经有指向父节点的指针,为什么不这样做: (与古怪的加拿大所说的相似,但…)

    创建每个节点的父节点的列表,在每个阶段按顺序构建列表。因此,
    list_a
    list_b
    会随着你走得越高而增长。将每个列表中最后添加的项目与另一个列表中的项目进行比较。只要列表_a中的某个项与列表_b中的某个项匹配,您就拥有了最低的共同祖先

    while (parent_a not in list_b) or (parent_b not in list_a):
        ...
    

    你不需要一直重新构建链直到根。在任何情况下,都必须按顺序(向前或向后)检查每个父级。

    Python有内置集。为什么不使用以下内容(伪代码):

    a_=set()
    而a:
    a_.添加(a)
    a=a.父母
    而b:
    如果a_中的b:
    返回b
    b=b.父母
    
    返回None#如果必须,您可以在不修改树本身的情况下计算深度。只需使用递归函数或迭代节点的祖先。是否有任何方法可以修改此函数,使其不需要两个循环?如果无法获得深度?上面的算法根本没用。但是,即使树中没有存储深度,在每一步维护父级列表并与其他父级进行比较也会得到结果。不过,这需要重复循环祖先列表,这可能会减慢速度。您可以编写一个简单的函数,通过递归或迭代祖先节点来获取深度。在节点/树上定义方法只是OO设计的一个很好的便利/特性。在这种情况下,将我们的答案与@weirdcanada的答案结合起来:在获取两个节点的深度(假设它没有存储在类/对象中)的同时,构建父节点列表。然后使用生成的较低深度(这是一个O(n^2)算法。我知道最初的海报上说他希望简单高于性能,但是如果树足够高,这个算法的性能会非常差。
    a_ancestors = set()
    while a:
      a_ancestors.add(a)
      a = a.parent
    
    while b:
      if b in a_ancestors:
        return b
      b = b.parent
    
    return None # <- can't reach that for a *tree* !!!
    
    visited = set()
    while a or b:
      if a:
        if a in visited:
          return a
        visited.add(a)
        a = a.parent
    
      if b:
        if b in visited:
          return b
        visited.add(b)
        b = b.parent
    
    return None # <- can't reach that for a *tree* !!!