【问题标题】:Lowest common ancestor of two leaves in a tree树中两片叶子的最低共同祖先
【发布时间】:2020-07-10 15:36:58
【问题描述】:

我有以下树形结构:

struct Node {
   int data;
   Node* parent = nullptr;
}

每个节点最多有一个父节点,但可以有多个子节点。我试图找到两个没有任何孩子的节点(node1 和 node2)的最低共同祖先。

这是我当前的代码:

std::vector<Node*> ancestors1;
std::vector<Node*> ancestors2;
temp_node = node1->parent;
while(temp_node!=nullptr) {
    ancestors1.push_back(temp_node);
    temp_node = temp_node->parent;
}
temp_node = node2->parent;
while(temp_node!=nullptr) {
    ancestors2.push_back(temp_node);
    temp_node = temp_node->parent;
}
Node* common_ancestor = nullptr;
if (ancestors1.size() < ancestors2.size()) {
    ptrdiff_t t = ancestors1.end() - ancestors1.begin();
    std::vector<Node*>::iterator it1 = ancestors1.begin();
    std::vector<Node*>::iterator it2 = ancestors2.end() - t;
    while(it1!=ancestors1.end()) {
        if (*it1 == *it2) {
            common_ancestor = *it1;
        }
        ++it1;
    }
} else {
    ptrdiff_t t = ancestors2.end() - ancestors2.begin();
    std::vector<Node*>::iterator it2 = ancestors2.begin();
    std::vector<Node*>::iterator it1 = ancestors1.end() - t;
    while(it2!=ancestors2.end()) {
        if (*it1 == *it2) {
            common_ancestor = *it1;
        }
        ++it2;
    }
}
return common_ancestor

这段代码并不总是有效,我不知道为什么。

【问题讨论】:

  • 请尝试创建“并不总是有效”的最小和最简单的树,并使用调试器逐步检查代码以帮助找出问题所在。
  • 创建函数来列出祖先而不是重复逻辑。顺便说一句,你有错字,因为你把 node2 的祖先放在 ancestors1
  • 只有一个迭代器移动,所以你只检查一个祖先。
  • 只需在 if-else 块的两侧执行++it1; ++it2;
  • it2 = ancestors2.end() - t - 为什么?两条路径都必须从根目录开始。难道你不能从一开始就迭代两条路径并在它们不同时立即突破吗?

标签: c++ algorithm tree


【解决方案1】:

对不起,我忍不住了。

除了错别字和错误,我相信它可以看起来更简单:

#include <cassert>
#include <algorithm>
#include <iostream>
#include <vector>

struct Node {
  int data;
  Node *parent = nullptr;
};

Node* findCommonAncestor(Node *pNode1, Node *pNode2)
{
  // find paths of pNode1 and pNode2
  std::vector<Node*> path1, path2;
  for (; pNode1; pNode1 = pNode1->parent) path1.push_back(pNode1);
  for (; pNode2; pNode2 = pNode2->parent) path2.push_back(pNode2);
  // revert paths to make indexing simple
  std::reverse(path1.begin(), path1.end());
  std::reverse(path2.begin(), path2.end());
  // compare paths
  Node *pNode = nullptr; // ancestor
  size_t n = std::min(path1.size(), path2.size());
  for (size_t i = 0; i < n; ++i) {
    if (path1[i] == path2[i]) pNode = path1[i];
    else break;
  }
  // done
  return pNode;
}

int main()
{
  // sample tree:
  /*     1
   *     |
   *     2
   *    / \
   *   3   4
   *       |
   *       5
   */
  Node node1 = { 1, nullptr };
  Node node2 = { 2, &node1 };
  Node node3 = { 3, &node2 };
  Node node4 = { 4, &node2 };
  Node node5 = { 5, &node4 };
  Node *pNode = findCommonAncestor(&node3, &node5);
  if (pNode) {
    std::cout << "Lowest common ancestor: " << pNode->data << '\n';
  } else {
    std::cout << "No common ancestor found!\n";
  }
}

输出:

Lowest common ancestor: 2

Live Demo on coliru

注意:

虽然使用iterators 有助于保持代码的通用性……

我认为这是坚持使用普通旧数组(又名std::vector)索引可以简化事情的一种情况。

【讨论】:

  • 确实简单多了。谢谢。
【解决方案2】:

我发现了问题。除了需要移动两个迭代器而不是一个(感谢 Jarod42 和 v78)之外,我还需要在找到最低共同祖先后立即退出 while 循环(否则它会返回最高共同祖先)。

while(it1!=ancestors1.end()) {
        if (*it1 == *it2) {
            common_ancestor = *it1;
            break;
        }

【讨论】:

    【解决方案3】:

    在那个时候你应该不需要任何额外的空间来解决这个问题:

    // measure depths
    size_t depth1=0;
    for (Node *n = node1; n; n=n->parent, ++depth1);
    size_t depth2=0;
    for (Node *n = node2; n; n=n->parent, ++depth2);
    
    // move the deeper one up until they're the same depth
    for (;depth1 > depth2; node1 = node1->parent, --depth1);
    for (;depth2 > depth1; node2 = node2->parent, --depth2);
    
    // move them both up until they match
    while(node1 != node2) {
        node1 = node1->parent;
        node2 = node2->parent;
    }
    
    return node1;
    

    【讨论】:

      猜你喜欢
      • 2011-07-28
      • 2012-11-08
      • 2014-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多