您的位置:首页 > 其它

[leetcode] 236.Lowest Common Ancestor of a Binary Tree

2015-08-21 11:35 316 查看
题目:

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

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.

题意:

给定一棵二叉树,查找其中两个节点的最近公共祖先。

思路:

拿到这道题首先分析下,什么是最近公共祖先。如果A是p和q最近的公共祖先,那么分为以下四种情况:

p,q分别在A的两个子树中。

p就是A,q在A的某个子树中。

q就是A,p在A的某个子树中。

p,q都是A。

方法一:如果B是A的祖先,那么很明显B是p,q的祖先。不妨假设此时A在B的左子树中,那么很明显不满足上面四个条件之一。此时继续查看B的左右孩子,然后发现A也是左右子树的祖先,继续往下判断,发现A的两个孩子都不是p,q的公共祖先,那么就可以得到A就是最近公共祖先。总结起来这个方法就是从根节点判断,当前节点是不是p,q的祖先,如果是的话查看当前节点的孩子是不是p,q的公共祖先,如果当前节点是p,q的公共祖先,但是它的所有孩子都不是,那么就可以断定当前节点就是最近公共祖先。不过这个方法的一个问题就是会去重复查找p,q是否是其子孙。比如对于当前节点能够访问到p的路径是A->B->C->D->p,同理访问q是A->B->C->E->q,那么接下里要去查看B是不是两个孩子的路径,又会访问B->C->D->p, B->C->E->q,其实这条路径之前已经走过,但是每次却在重复通过这些路径查找。

方法二:

所以我们需要进行方法的改良,我们可以从根节点保存访问到p以及q的路径。比如以上所说的A->B->C->D->p和A->B->C->E->q,那么从根节点依次往后扫描,到最后一个相同的元素(C),就是两个点的最近公共祖先。这道题目需要额外的空间平均情况是O(lgn),最坏是O(n),

综上,就是先找到根节点到两个节点的路径,然后找到路径上最后一个共同的节点。

以上。

代码如下:

/**
1. Definition for a binary tree node.
2. struct TreeNode {
3.     int val;
4.     TreeNode *left;
5.     TreeNode *right;
6.     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
7. };
*/
class Solution
{
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if(root == NULL || p == NULL || q == NULL) return NULL;
vector<TreeNode*> pv, qv;
if(!(getPath(root, p, pv) && getPath(root, q, qv)))return NULL;
return getCommonAncestor(pv, qv);
}
bool getPath(TreeNode* root, TreeNode* p, vector<TreeNode*>& elements)
{
elements.push_back(root);
if(root == p)
{
return true;
}
if(root->left != NULL)
{
if(getPath(root->left, p, elements))return true;
}
if(root->right != NULL)
{
if(getPath(root->right, p, elements))return true;
}
elements.pop_back();
return false;
}
TreeNode* getCommonAncestor(vector<TreeNode*> pv, vector<TreeNode*> qv)
{
TreeNode* last = NULL;
int index = 0;
int sp = pv.size();
int sq = qv.size();
while(index < sp && index < sq && pv[index] == qv[index])
{
last = pv[index];
index++;
}
return last;
}
};


方法三:

以上需要保存额外的空间,那么能否不需要保存从根节点到p,q的路径呢?答案是肯定的。采用递归的方法,

1.如果当前节点的左子树中找到了最近祖先,ok,直接返回。

2.如果左子树没找到,但是右子树找到了,ok继续直接返回。

3.如果左右子树都没找到最近祖先,那么有可能当前节点就是公共祖先(为什么是可能呢,因为如果p或者q有一个或者两个根本就不在树中,那自然不能说当前节点就是公共祖先了)。

4.如果左右子树各找到p,q中的一个,那么当前节点就是最近祖先。

5.如果两边一个都没找到,但是当前节点root==p==q,那么当前节点也确实就是最近祖先节点。

6.否则就是返回NULL,找不到最近祖先。

我们在递归的函数中,加入一个保存最近祖先的值。这样就可以帮助判断是否已经找到了最近祖先,找到了就直接返回啦。

以上。

代码如下:

/**
* Definition for a binary tree node.
* struct TreeNode {
*     int val;
*     TreeNode *left;
*     TreeNode *right;
*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution
{
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if (root == NULL || p == NULL || q == NULL) return NULL;
TreeNode* result = new TreeNode(0);
result = NULL;
getAncestor(root, p, q, &result);
return result;
}
TreeNode* getAncestor(TreeNode* root, TreeNode* p, TreeNode* q, TreeNode** ancestor) {
if (*ancestor != NULL)return *ancestor;
if (root == NULL)return NULL;
if (root == p && root == q) {
*ancestor = root;
return root;
}
TreeNode* res1 = getAncestor(root->left, p, q, ancestor);
if (*ancestor != NULL)return *ancestor;
TreeNode* res2 = getAncestor(root->right, p, q, ancestor);
if (*ancestor != NULL)return *ancestor;
if (res1 != NULL && res2 != NULL) {
*ancestor = root;
return *ancestor;
}
else if (res1 != NULL || res2 != NULL) {
if (root == p || root == q) {
*ancestor = root;
return *ancestor;
}
else return(res1 == NULL) ? res2 : res1;
}
else if (root == p || root == q)return root;
return NULL;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: