【问题标题】:Finding Least Common Ancestor in Binary Tree [duplicate]在二叉树中找到最小共同祖先[重复]
【发布时间】:2012-08-16 20:52:35
【问题描述】:

可能重复:
How can I find the common ancestor of two nodes in a binary tree?
first common ancestor of a binary tree

我有一棵二叉树,如下所示。我需要找到最小共同祖先(LCA)。例如,6 和 4 的 LCA 为 1,4 和 5 的 LCA 为 2。

    1
   / \
  2   3
 / \ / \
4  5 6  7 

谁能建议我应该如何处理和解决这个问题?

【问题讨论】:

标签: java algorithm tree binary-tree


【解决方案1】:

从普通的深度优先搜索算法开始:

public Node find(Node node, int target) {
    if(node == null || node.value == target) {
        return node;
    }
    if(node.value > target) {
        return find(node.left, target);
    } else {
        return find(node.right, target);
    }
}

现在,调整它以采用两个“目标”参数,target1 和 target2。

当搜索 target1 将您带到左侧,搜索 target2 将您带到右侧时,您已找到 LCA。

这假设两个目标都确实存在。如果您需要断言它们确实存在,则需要在找到潜在的 LCA 后继续搜索。

public Node findLca(Node node, int t1, int t2) {
    if(node == null) {
        return null;
    }
    if(node.value > t2 && node.value > t1) {
        // both targets are left
        return findLca(node.left, t1, t2);
    } else if (node.value < t2 && node.value < t1) {
        // both targets are right
        return findLca(node.right, t1, t2);
    } else {
        // either we are diverging or both targets are equal
        // in both cases so we've found the LCA
        // check for actual existence of targets here, if you like
        return node;
    }
}

【讨论】:

  • 我真的很喜欢你的解释。但是在 find 方法中你不应该有 if(node==null) return null; ?
  • @Student 我有if(node==null) return node,这是等价的。
  • 这不是假设树是 BST 吗?
  • 这不能回答问题。您正在进行深度优先搜索,这在尝试获取节点的祖先时没有任何意义,因为您需要遍历节点的父节点以进行 LCA 算法。 DPS 遍历节点的子节点。 LCA问题是要得到最小共同祖先,所以必须遍历父母。
【解决方案2】:

使用列表可以解决您的问题。

你应该创建一个 getAncestorList()。它按其祖先返回列表顺序,例如。 4 有祖先列表 [1,2],7 有祖先列表 [1,3]

list1 = node1.getAncestorList()
list2 = node2.getAncestorList()

minlength = min(list1.size(), list2.size())
for (int i = 0; i < minlength; i++) {
    e1 = list1.getItemAt(i);
    e2 = list2.getItemAt(i);
    if (e1 == e2) ec = e1;
}
return ec;

因为它们都有相同的根祖先。所以你不需要关心不同的深度。你总能找到前(n)个相同的祖先。而祖先(n)是最近的共同祖先。

【讨论】:

  • 如果两个输入节点不在同一深度怎么办?您需要一种方法来确定两个祖先列表的分歧点。
  • @chepner,简单地说,“比较每个元素”步骤需要处理不同长度的列表。它是“找到这两个列表中存在的最大数字”
  • 最小的共同元素。我们确定每个节点都有唯一的编号吗?如果不是,则第一个共同祖先的每个子树中可能有相同的数字。
  • 是的,让我们注意“数字”的含义。 OP 图中的节点 ID 始终以 1 作为根,但你是对的,最小的公共
  • 我们可以确定节点对象是唯一的。我认为上面的那些“数字”只是不同节点的简称。
【解决方案3】:

这是我通常做的:

首先计算f[i][j],它表示节点i的第2^j-th父亲。我们有

f[i][j] = f[f[i][j - 1]][j - 1]

现在我们可以在log(n)时间得到节点i的j-th父亲。

我们需要每个节点的深度,比如h[i]

以上可以在简单的dfs() 中完成,复杂度为O(N*Log(N))

然后对于每个查询(i,j)询问节点(i)和节点(j)的 LCA,想象有两只猴子从树上爬起来,试图到达同一个节点。

  1. 首先让它们处于相同的高度,然后我们知道它们需要起床 相见的高度相同。
  2. 虽然他们不在同一个节点,但尽可能爬升。
  3. 他们当前所在节点的父亲是 LCA。

你可以参考这个:

int query(int u, int v){
    if(h[u]>h[v])swap(u,v);
    v = getUp(v,h[v]-h[u]);
    for(int i=log(n);i>=0;i--){
        if(f[u][i]!=f[v][i]){
            u=f[u][i];
            v=f[v][i];
        }
    }
    while(u!=v){
        u=f[u][0];
        v=f[v][0];
    }
    return u;
}

这里getUp(i, j)的意思是找到节点i的j-th的父亲,就像我们上面提到的,可以是

int nt(int u,int x){
    for(int i=log(n);i>=0;i--){
        if((1<<i)<=x){
            u=f[u][i];
            x-=(1<<i);
        }
    }
    return u;
}

所以对于非常查询,复杂度也是O(N*Log(N))

【讨论】:

    猜你喜欢
    • 2011-09-04
    • 2011-07-28
    • 2012-01-16
    • 2012-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    相关资源
    最近更新 更多