从root开始遍历,如果n1和n2中的任一个和root匹配,那么root就是LCA。 如果都不匹配,则分别递归左、右子树,如果有一个 key(n1或n2)出现在左子树,并且另一个key(n1或n2)出现在右子树,则root就是LCA. 如果两个key都出现在左子树,则说明LCA在左子树中,否则在右子树。
/* 只用一次遍历解决LCA */ #include <iostream> using namespace std; struct Node { struct Node *left, *right; int key; }; Node* newNode(int key) { Node *temp = new Node; temp->key = key; temp->left = temp->right = NULL; return temp; } // 返回n1和n2的 LCA的指针 // 假设n1和n2都出现在树中 struct Node *findLCA(struct Node* root, int n1, int n2) { if (root == NULL) return NULL; // 只要n1 或 n2 的任一个匹配即可 // (注意:如果 一个节点是另一个祖先,则返回的是祖先节点。因为递归是要返回到祖先的 ) if (root->key == n1 || root->key == n2) return root; // 分别在左右子树查找 Node *left_lca = findLCA(root->left, n1, n2); Node *right_lca = findLCA(root->right, n1, n2); // 如果都返回非空指针 Non-NULL, 则说明两个节点分别出现了在两个子树中,则当前节点肯定为LCA if (left_lca && right_lca) return root; // 如果一个为空,在说明LCA在另一个子树 return (left_lca != NULL)? left_lca: right_lca; } //测试 int main() { // 构造上面图中的树 Node * root = newNode(1); root->left = newNode(2); root->right = newNode(3); root->left->left = newNode(4); root->left->right = newNode(5); root->right->left = newNode(6); root->right->right = newNode(7); cout << "LCA(4, 5) = " << findLCA(root, 4, 5)->key; cout << "\nLCA(4, 6) = " << findLCA(root, 4, 6)->key; cout << "\nLCA(3, 4) = " << findLCA(root, 3, 4)->key; cout << "\nLCA(2, 4) = " << findLCA(root, 2, 4)->key; return 0; }
时间复杂度为O(n),但是上面的方法还是有所局限的,必须保证两个要查找的节点n1和n2都出现在树中。如果n1不在树中,则会返回n2为LCA,理想答案应该为NULL。要解决这个问题,可以先查找下 n1和n2是否出现在树中,然后加几个判断即可。