<span style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; background-color: rgb(255, 255, 255);">Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.</span>
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”
_______3______ / \ ___5__ ___1__ / \ / \ 6 _2 0 8 / \ 7 4
For example, the lowest common ancestor (LCA) of nodes 5
and 1
is 3
. Another example is LCA of nodes 5
and 4
is 5
, since a node can be a descendant of itself according to the LCA definition.
找到二叉树中两个节点的最低公共祖先(LCA)
其实这道题之前有一个铺垫就是找到二叉查找树的LCA,根据二叉查找树的性质,如果某个节点的值位于两个目标节点值的中间,那么这个节点必然是LCA。为什么?因为如果某个节点的值同时大于或者小于目标节点的值,那么这个节点必然位于两个目标节点的右方或者左方,不可能是最低公共子节点。
那么对于这棵普通二叉树来说应该怎么做呢,好像乍一想没什么思路,也找不到什么像BST那样的性质....
方法一:
不要着急,最容易想到的就是从根节点遍历,分别找到根节点到这两个目标节点的路径保存下来,然后问题就变得简单了,两条路径上最后一个相同的节点就是LCA。下边是代码
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (NULL == root || NULL == p || NULL == q) return NULL;
std::vector<TreeNode *> pathP, pathQ;
pathP.push_back(root);
pathQ.push_back(root);
findPath(root, p, pathP);
findPath(root, q, pathQ);
TreeNode* result = NULL;
for (int i = 0; i < min(pathP.size(), pathQ.size()); i++){
if (pathP[i] == pathQ[i]) result = pathP[i];
else break;
}
return result;
}
bool findPath(TreeNode* root, TreeNode* node, std::vector<TreeNode *>& path){
if (root == node) return true;
if (root->left){
path.push_back(root->left);
if (findPath(root->left, node, path)) return true;
path.pop_back();
}
if (root->right){
path.push_back(root->right);
if (findPath(root->right, node, path)) return true;
path.pop_back();
}
return false;
}
};
上面找路径用了一种递归的方式,如果有更好的方法,欢迎建议。
方法二:还是得从树的本身出发,找性质。
1、如果某个节点是两个目标节点的LCA,那么这两个节点肯定分居这个节点两侧或者这个节点就是目标节点之一
2、如果一个节点是CA但不是LCA,那么LCA一定在他的左子树或者右子树,并且两个目标节点都在这个节点的一侧
从这个角度出发,可以引入一种方法,count某个节点为根的子树包含目标节点的个数,如果某个节点左子树中有一个,右子树中有一个,那么这个节点就是要找的节点,如果某一侧有2个,就继续往这一侧寻找。
上代码:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (NULL == root || NULL == p || NULL == q) return NULL;
if (root == p || root == q) return root;
int matches = countMatches(root->left,p,q);
if(matches == 1) return root;
else if(matches == 2) return lowestCommonAncestor(root->left,p,q);
else return lowestCommonAncestor(root->right,p,q);
}
int countMatches(TreeNode* root, TreeNode* p, TreeNode* q){
if(NULL == root) return 0;
int matches = countMatches(root->left,p,q) + countMatches(root->right,p,q);
if(root == p || root == q) return matches + 1;
else return matches;
}
};
方法三:从第二种方法延伸而来,也是一种递归的思路。上一中是自顶向上的,这样会树中的某些节点会被遍历很多遍,方法三是一种自底向上递归的思路,从下往上找,并记录当前找到的目标节点个数,第一个个数为2的节点就是LCA
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (NULL == root || NULL == p || NULL == q) return NULL;
if (root == p || root == q) return root;
TreeNode *ancestor = NULL;
countMatches(root, p, q, ancestor);
return ancestor;
}
int countMatches(TreeNode* root, TreeNode* p, TreeNode* q, TreeNode *&ancestor){
if(NULL == root) return 0;
int matches = (root == p || root == q)? countMatches(root->left,p,q,ancestor) + countMatches(root->right,p,q,ancestor) + 1 : countMatches(root->left,p,q,ancestor) + countMatches(root->right,p,q,ancestor);
if(matches == 2 && ancestor == NULL) ancestor = root;
return matches;
}
};
这道题纠结了两天,但也理解的不够清楚,如果有问题欢迎指正!