给定二叉查找树中的两个节点,求它们的最近公共祖先(Lowest Common Ancestor - LCA)。
在详细介绍之前,可以先参考下这篇文章"二叉树(70) - 最近公共祖先[1]"
函数原型定义如下:
Node *getLCA(Node* root, int n1, int n2) 其中n1和n2是指定的两个节点值。
例如, 上述BST中,10和14的LCA是12,而8和14的LCA是8。
下面是来自Wikipedia的关于LCA的定义:
假设存在一颗二叉树T, 其中节点n1和n2的最小共同祖先的定义为,T中最小的节点,它包含了n1和n2做为后代(同时规定一个节点自己可以做为自己的后代).
节点n1和n2的LCA是一个共同的祖先,距离root根节点最远。对于LCA进行计算是很有用的,例如,可以计算树中一对节点之间的距离:n1到n2的距离为n1到root的距离,加上n2的root的距离,再减去它们的LCA到root距离的两倍。
解决方法:
可以利用BST的特性。从root节点开始进行递归遍历。此方法的主要思路是,当从top遍历至buttom时,第一次遇到的节点n,且n满足n1<n<n2,或者n等于n1或n2,则n是n1和n2的LCA。安装这种方法递归的遍历,当节点值比n1和n2都大时,则LCA位于此节点的左子树中;如果节点值小于n1和n2,则LCA位于此节点的右子树中。否则root就是LCA。
// C++程序,求BST这两个节点的LCA #include <iostream> struct Node { int key; Node *left; Node *right; }; //求n1和n2的LCA。假设n1和n2都位于BST中。 Node *getLCA(Node* root, int n1, int n2) { if (root == NULL) return NULL; // 如果n1和n2小于root,则LCA位于左子树中 if (root->key > n1 && root->key > n2) return getLCA(root->left, n1, n2); // 如果n1和n2大于root,则LCA位于右子树中 if (root->key < n1 && root->key < n2) return getLCA(root->right, n1, n2); return root; } // 创建一个新的BST节点 Node *createNewNode(int item) { Node *temp = new Node; temp->key = item; temp->left = temp->right = NULL; return temp; } int main() { /* 20 / 8 22 / \ 4 12 / 10 14 */ Node *root = createNewNode(20); root->left = createNewNode(8); root->right = createNewNode(22); root->left->left = createNewNode(4); root->left->right = createNewNode(12); root->left->right->left = createNewNode(10); root->left->right->right = createNewNode(14); int n1 = 10, n2 = 14; Node *t = getLCA(root, n1, n2); printf("LCA of %d and %d is %d \n", n1, n2, t->key); n1 = 14, n2 = 8; t = getLCA(root, n1, n2); printf("LCA of %d and %d is %d \n", n1, n2, t->key); n1 = 10, n2 = 22; t = getLCA(root, n1, n2); printf("LCA of %d and %d is %d \n", n1, n2, t->key); return 0; }
输出:
LCA of 10 and 14 is 12
LCA of 14 and 8 is 8
LCA of 10 and 22 is 20
时间复杂度: O(h),其中h是树的高度.
另外,上面程序还需要额外的内存来进行递归的函数调用栈。可以使用下面的遍历方法来避免额外的内存空间。
//求节点n1和n2的LCA Node *getLCA(Node* root, int n1, int n2) { while (root != NULL) { // 如果n1和n2小于root,则LCA位于左子树中 if (root->data > n1 && root->data > n2) root = root->left; // 如果n1和n2大于root,则LCA位于右子树中 else if (root->data < n1 && root->data < n2) root = root->right; else break; } return root; }
时间: 2024-12-13 06:22:36