从邻接列表中找到两个节点的最低公共祖先

时间:2013-09-28 10:22:05

标签: algorithm data-structures graph tree breadth-first-search

如果我知道树中存在的每个节点的邻接列表,那么如何找出该树中存在的任何两个节点的最低共同祖先?

实际上我想找出任意两个节点之间的距离,所以我想计算LCA。有没有办法从邻接列表中计算它?

n中的n1和n2的LCA是n1的共同祖先,n2位于离根最远的位置。最低共同祖先的计算可能是有用的,例如,作为确定树中节点对之间距离的过程的一部分:从n1到n2的距离可以计算为从根到n1的距离加上距离从根到n2,减去从根到其最低共同祖先的距离的两倍。 (Source Wiki

3 个答案:

答案 0 :(得分:1)

我们处理邻接列表的事实并没有真正改变问题。

找到节点A和B的LCA的基本思路如下:

  • 从根开始。
  • 如果子项的子树同时包含A和B,则返回该子树的LCA。
  • 如果孩子包含A而另一个孩子包含B。

通过为任何一种情况返回一个指标,上述检查可以很容易地合并到一个函数中。

在无序树中,您必须在最坏的情况下探索整个树,但在二进制搜索树中,您可以通过简单地将其值与当前节点进行比较,轻松检查左子树或右子树是否可以包含节点

但实际上你不应该使用LCA算法来确定距离。您应该修改上述内容以返回距离而不是LCA。这样做的修改相当简单:

  • 如果您已经在孩子的子树中找到了距离,请将其退回。
  • 如果您在单独的子女的子树中找到了A和B,请返回A和B之间的距离。
  • 如果您只在儿童的子树中找到A或B,只需返回A或B的距离,并附上指示符,说明您要返回的内容。

答案 1 :(得分:0)

您可以尝试以下方式:

class Node
{
public:
    // Other stuff.
    const Node* getParent() const { return parent; }
private:
    Node* parent;
    std::vector<Node*> children;
};

const Node* getLowestCommonAncestor(const Node& lhs, const Node& rhs)
{
    for (const Node* node1 = &lhs; node1 != nullptr; node1 = node1->getParent()) {
        for (const Node* node2 = &rhs; node2 != nullptr; node2 = node2->getParent()) {
            if (node1 == node2) {
                return node1;
            }
        }
    }
    return nullptr;
}

或者,如果您没有父母:

namespace detail
{
    struct LCAFlag {
        enum { NoFound = 0, leftFound = 1, rightFound = 2 };
    };

    const Node* getLCA_Rec(const Node& root, const Node& lhs, const Node& rhs, unsigned int& flag)
    {
        if (&root == &lhs) {
            flag |= LCAFlag::leftFound;
        } else if (&root == &rhs) {
            flag |= LCAFlag::rightFound;
        }
        if (flag == (LCAFlag::leftFound | LCAFlag::rightFound)) {
            return nullptr; // both found. parent is the LCA
        }
        for (auto it = root.children.begin(); it != root.children.end(); ++it) {
            const Node* res = getLCA_Rec(**it, lhs, rhs, flag);

            if (res != nullptr) {
                return res;
            }
            if (flag == (LCAFlag::leftFound | LCAFlag::rightFound)) {
                return &root;
            }
        }
        return nullptr;
    }
}
const Node* getLCA(const Node& root, const Node& lhs, const Node& rhs)
{
    unsigned int flag = detail::LCAFlag::NoFound;
    return detail::getLCA_Rec(root, lhs, rhs, flag);
}

答案 2 :(得分:0)

  1. 保持每个节点的高度(与根的距离)。
  2. 如果两个节点的高度不同,请从更深的节点向上走,直到我们在同一级别上有节点。
  3. 如果两个节点相等,那么我们就得到了答案。如果没有,请上升到另一个级别并重复。
  4. 这假设您的树是 rooted ,并且您可以容纳一些额外的空格来存储每个节点的高度和父指针。

    该算法的效率为O(高度),因此取决于树的平衡程度。