最低共同祖先算法

时间:2011-06-14 02:31:09

标签: algorithm tree traversal least-common-ancestor

所以我一直在研究实现最低共同的祖先算法。我查看了许多不同的算法(主要是Trajan解决方案的变体或RMQ的变体)。

我使用的是非二叉树。我的树通常会在查询之间进行更改,因此预处理不一定是值得的。树不应超过50-75个节点。我想知道的是我是否应该使用他们的算法或只是坚持自己的算法。

我的算法

myLCA(node1, node2) {
    parentNode := [ ]
    while (node1!=NULL) {
         parentNode.push(node1)
         node1 := node1.parent
    }
     while (node2!=NULL) {
         for i in parentNode.size {
             if (parentNode(i) == node2) {
                 return node2; 
             }
         }
         node2 := node2.parent
     }

}       

6 个答案:

答案 0 :(得分:15)

正如其他人所提到的,您的算法目前是二次的。对于小到50-75个节点的数据集来说,这可能不是问题,但在任何情况下都可以直接将其更改为线性时间而不使用任何集合或哈希表,只需记录每个节点的根的完整路径,然后从根向后走,寻找第一个不同的节点。前一个节点(这两个不同节点的共同父节点)就是LCA:

linearLCA(node1, node2) {
    parentNode1 := [ ]
    while (node1!=NULL) {
         parentNode1.push(node1)
         node1 := node1.parent
    }
    parentNode2 := [ ]
    while (node2!=NULL) {
         parentNode2.push(node2)
         node2 := node2.parent
    }
    while (node1 == node2 && !isEmpty(parentNode1) && !isEmpty(parentNode2)) {
        oldNode := node1
        node1 := parentNode1.pop()
        node2 := parentNode2.pop()
    }
    if (node1 == node2) return node1    // One node is descended from the other
    else return oldNode                 // Neither is descended from the other
}

EDIT 27/5/2012:处理一个节点从另一个节点下降的情况,否则会导致尝试pop()空堆栈。感谢该死的指责。 (我也意识到跟踪单个oldNode就足够了。)

答案 1 :(得分:3)

对于那么小的树,我不打算执行任何更复杂的事情。您的解决方案看起来不错,尽管时间复杂度是根据树的高度来平方的。如果您可以轻松实现Set(大多数语言都内置了),那么算法可以调整为,

  1. 从第一个节点到根节点进行遍历并收集集合中的所有节点
  2. 从第二个节点到根节点进行遍历,并检查该集合中是否存在当前节点。如果确实如此,那就是共同的祖先。
  3. 此外,该算法假定节点可以是其自己的祖先。否则,您必须稍微调整算法。考虑这个例子,

    A
    |
    B
    |
    C
    

    当试图找到B和C的最低共同祖先时,该算法会报告B,根据你如何定义祖先,这可能是也可能不是真的。

答案 2 :(得分:2)

在不查看任何一种算法的细节的情况下,我建议考虑这种算法的效率对整个应用程序的重要性,以及实现其他算法需要多少工作量。

此算法在应用程序的正常(或强调)操作中运行多少次?它会导致用户等待的时间超过必要的时间吗?其他算法的速度是否比您的快得多? (熟悉算法的人可以就此给出更详细的答案。)

我认为不值得优化一些代码,除非你会看到相当大的结果(有些人非常强烈地认为过早的optamisation是the root of all evil

答案 3 :(得分:1)

你的算法是二次的,但很容易变成线性的。

只需使用哈希表(即设置)代替parentNode,而不是列表。 因此,检查节点是否在parentNode中将是O(1)而不是O(n)

答案 4 :(得分:1)

我刚刚撰写了一篇博客文章,介绍了如何针对此问题实现自己的算法,但扩展到了一组具有任意长度的节点。 你可以在这里找到它(有关它是如何工作的逐步图解说明)

http://bio4j.com/blog/2012/02/finding-the-lowest-common-ancestor-of-a-set-of-ncbi-taxonomy-nodes-with-bio4j/

干杯,

巴勃罗

答案 5 :(得分:0)

我有一个简单的解决方案 排序这两个元素,最低的是左边,最高的是右边 访问root def recurse(root) 如果root.empty返回nil? 如果离开< = root&&对> = root   返回root elsif left< = root&&对< = root   递归(root.left) 其他   递归(root.right) 端

因此,这将检查每次遍历 在平均值和最差值的O(log n)时间内解决的问题和O(log