【问题标题】:modification to LCA code for binary tree to check if node is present in Java修改二叉树的 LCA 代码以检查 Java 中是否存在节点
【发布时间】:2014-01-29 17:34:04
【问题描述】:

我有这段代码,它计算Binary tree 中给定两个nodesLeast common Ancestor。 目前,它假设两个节点都存在。我可以编写一个辅助方法来检查节点是否存在,然后调用LCABT 方法。那将需要遍历树两次。我想知道是否有一种方法可以检查和处理当前代码中不存在节点的情况。

//LCA of Binary tree
            public static Node LCABT(Node root, int v1, int v2){
                if (root==null)
                    return null;
                if (root.data==v1 || root.data==v2){
                    return root;
                }
                Node left = LCABT(root.left,v1,v2);
                Node right = LCABT(root.right,v1,v2);

                if(left!=null && right!=null)
                    return root;
                else if (left!=null)
                     return left;
                else  if (right!=null)
                       return right;
                return null;
            }

【问题讨论】:

  • @NiklasB。 “而不是 O(log n),这是可能实现的”——这仅在平衡二叉 搜索树中,或者如果您知道节点所在的位置。
  • @Dukeling:是的,我现在明白了。我假设有一棵搜索树,但它可能不是一棵。算法仍然不应该以这里介绍的形式工作。
  • @NiklasB.:我不明白你的意思。但是现在的代码可以工作。如果两者都不存在,我返回 null。但是如果其中一个存在而另一个不存在,我返回作为节点存在的那个,这是不正确的
  • 忽略我的愚蠢。我无法阅读代码。至于您的问题,如果v1 != v2` 并且两者都存在,您将在某个时候遇到if(left!=null && right!=null) 的情况。您可以只记住这是否发生或在递归期间将其传递到调用堆栈。如果不是,则树中最多存在一个节点(假设树中没有重复节点)。
  • 问题是如果一个存在而另一个不存在。例如,我有 root.data=5、v1=5 和 v2=10,假设 v2 不存在。我当前的代码,只是从 root.data=v1 返回 root,但它没有检查 v2 是否存在..希望我有意义..?

标签: java algorithm binary-tree least-common-ancestor


【解决方案1】:

使函数返回一对(state, lca)state 必须是以下之一:

0: Neither v1 nor v2 appear at or under root; lca is meaningless.
1: Only v1 appears at or under root; lca is meaningless.
2: Only v2 appears at or under root; lca is meaningless.
3: Both v1 and v2 appear at or under root, and have LCA equal to lca.

函数应该从检查基本情况开始:

LCABT(Node root, int v1, int v2) {
    if (root == null) then return (0, null);

否则,递归它的左右孩子,看看他们中的一个是否自己解决了问题:

    (s1, lca1) = LCABT(root.left, v1, v2);
    (s2, lca2) = LCABT(root.right, v1, v2);

如果s1s2 为3,则已找到LCA(分别为lca1lca2)并且可以立即返回。 (事实上​​,你甚至可以通过在第二次调用LCABT()之前检查s1 == 3来获得加速:如果是,那么我们已经有了LCA,不需要第二次调用。)

    if (s1 == 3) then return (3, lca1);
    if (s2 == 3) then return (3, lca2);

否则,设置s = s1 | s2(即按位或)。如果s == 3 那么我们知道root 是 LCA,但我们还没有考虑所有方式它可以是 LCA:当只有 @ 之一时它仍然可以是 LCA 987654336@ 和 v2 存在于其子代或其下,前提是另一个值位于 root 本身:

    s = s1 | s2;
    if (root.data == v1) then s = s | 1;
    if (root.data == v2) then s = s | 2;

现在所有root 是 LCA 的情况都暗示s == 3,所以如果s == 3 那么我们可以立即返回(3, root)

    if (s == 3) then return (3, root);

否则,v1v2 中最多有一个在或低于root,所以我们应该返回一个值来指示它是哪一个:

    return (s, null);
}

最后,对LCABT() 的原始顶级调用显然应该认为该函数只有在返回state 值为3 时才成功。

与您的算法相比,此算法的另一个优势是它不会被树中 v1v2 的重复副本所欺骗。

【讨论】:

  • 按位或运算开始时我迷路了。之后我很难跟上。为什么要设置 s = s1 | s2。我猜运行时间仍然是 O(n)
  • 因为当且仅当 s1 和 s2 之一为 1 而另一个为 2 时 s 才等于 3。
  • 更冗长的方式是:a = min(s1, s2); b = max(s1, s2); if (a == 1 && b == 2) then s = 3 else s = b;。一个更冗长的方法是写出一套完整的if 语句,但我不会那样做——如果它仍然没有点击,就写出真值表。是的,运行时间是 O(n)。
  • 还有一点可能会有所帮助:我们将所有 state 值(包括 s)视为 2 位“位集”。位 0(LSB)= 1 iff v1 出现在根或根下,位 1 = 1 iff v2 出现在根或根下。按位或对应于取 2 个位集的并集。
  • 我想if (root.data == v1) then s = s | 1; if (root.data == v2) then s = s | 2; 应该在(s1, lca1) = LCABT(root.left, v1, v2); (s2, lca2) = LCABT(root.right, v1, v2); 之前。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-18
相关资源
最近更新 更多