python最低共同祖先

时间:2012-08-10 16:59:18

标签: python algorithm tree

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

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

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

  3. 我该怎么办?我正在寻找一些有利于简化而不是性能的东西,因此需要复杂处理的解决方案已经完成。

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

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

6 个答案:

答案 0 :(得分:6)

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

对于每个节点,递归地向上遍历树,直到您到达根。在每个父节点上,将节点插入list。这应该会给您list_alist_b。迭代最短列表,比较每个列表中的元素。当您找到一个不匹配的条目时,前一个条目是您最大的父元素。

答案 1 :(得分:5)

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

您可以使用单个while循环执行此操作。一旦你选择了具有相同深度的祖先:

while (ancestorA !== ancestorB) {
    ancestorA = ancestorA.parent();
    ancestorB = ancestorB.parent();
}

当while循环终止时,ancestorAancestorB将各自成为您的共同祖先。

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

答案 2 :(得分:2)

Python有内置集。为什么不使用(伪代码)的东西:

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* !!!

这将构建节点 a 的所有祖先(无序)集合(包括 a 本身)。

然后,我们第二次循环遍历 b 的所有祖先。根据定义,作为 a 的祖先的 b 的第一个祖先将是第一个共同的祖先。这适用于 O(n)(空间和时间)

您可以通过同时收集 a b 的祖先集合来加速流程(最终以占用空间为代价) - 停止一找到公共节点。代码有点做作,因为你必须处理其中一个分支到达另一个分支之前:

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* !!!

答案 3 :(得分:0)

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

答案 4 :(得分:0)

想要维护父项集合,最好使用带有=的哈希映射,因为在这种情况下,您不会在父项列表中搜索登录。因此,在每次迭代时检查此映射,如果当前节点的父节点已存在于映射中,则此父节点是您的结果。在最坏的情况下,它会给出O(n),但是如果你在某些情况下开始对两个节点进行分析,你将能够更快地找到它。

答案 5 :(得分:0)

由于在两种情况下都已经有指向父节点的指针,为什么不这样做: (类似于weirdcanada所说的但是......)

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

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

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