要求考虑以下三种种情况,给出解决方案,并解决:
1:二叉树每个节点有parent(三叉链)
2:二叉树是搜索二叉树。
3:就是普通二叉树。(尽可能实现时间复杂度为O(N))
求两个结点的最近公共祖先有两种情况。
1、如果这两个结点不在一条线上,则它们就分别在最近的公共祖父的左右子树上。
2、如果这两个结点在一条线上,则它们之中深度最前的就是他们的最近的公共祖先。
1:三叉链。在这种情况下,我们可以分别用两个数据结构来保存两个节点到根节点的路径,然后通过对比,找到最近公共祖先。
/*
//三叉链
struct Node
{
int _data;
Node* _left;
Node* _right;
Node* _parent;
Node(const int& x)
: _data(x)
, _left(NULL)
, _right(NULL)
, _parent(NULL)
{}
};
*/
Node* GetLastestCommonAncestor4(Node* root, Node* x1, Node* x2)
{
assert(x1 && x2);
if (root == NULL)
return NULL;
stack<Node*> s1;
stack<Node*> s2;
//x1
Node* cur1 = x1;
while (cur1->_parent)
{
s1.push(cur1);
cur1 = cur1->_parent;
}
//x2
Node* cur2 = x2;
while (cur2->_parent)
{
s2.push(cur2);
cur2 = cur2->_parent;
}
//走到这里,两个栈中分别保存了x1和x2到根节点的路径
while (s1.size() != s2.size())
{
if (s1.size() > s2.size())
s1.pop();
else
s2.pop();
}
while (!s1.empty() && !s2.empty() && s1.top() != s2.top())
{
s1.pop();
s2.pop();
}
if (s1.top() == s2.top())
return s1.top();
return NULL;
}
2:二叉树是搜索二叉树,对于搜索树来说,左子树的节点都比根节点小,右子树的节点都比根节点大。根据搜索树这样的特征,我们可以有以下思路。
(1)如果一个节点比根节点小,另一个节点比根节点大, 则根节点为最近公共祖先;
(2)如果两个节点均比根节点小,则最近公共祖先在左子树;
(3)如果两个节点均比根节点大,则最近公共祖先在右子树。
有了这样的思路,我们可以通过递归,实现查找最近公共祖先。
struct Node
{
int _data;
Node* _left;
Node* _right;
Node(const int& x)
: _data(x)
, _left(NULL)
, _right(NULL)
{}
};
Node* GetLastestCommonAncestor1(Node* root, Node* x1, Node* x2)
{
assert(x1&&x2);
if (root == NULL)
return NULL;
if ((x1->_data <= root->_data&&x2->_data >= root->_data)
|| (x1->_data >= root->_data&&x2->_data <= root->_data))
return root;
else if (x1->_data < root->_data&&x2->_data < root->_data)
return GetLastestCommonAncestor1(root->_left, x1, x2);
else
return GetLastestCommonAncestor1(root->_right, x1, x1);
}
3:普通二叉树
可以分别使用上面两种特殊情况的思想来处理普通二叉树
struct Node
{
int _data;
Node* _left;
Node* _right;
Node(const int& x)
: _data(x)
, _left(NULL)
, _right(NULL)
{}
};
//普通二叉树(搜索二叉树思想)
bool IsInTree(Node* root, Node* x)
{
if (root == NULL)
return false;
if (root->_data == x->_data)
return true;
else
return IsInTree(root->_left, x) || IsInTree(root->_right, x);
}
Node* GetLastestCommonAncestor2(Node* root, Node* x1, Node* x2)
{
assert(x1 && x2);
if (root == NULL)
return NULL;
bool x1InLeft, x1InRight, x2InLeft, x2InRight;
x1InLeft = IsInTree(root->_left, x1);
x1InRight = IsInTree(root->_right, x1);
assert(x1InLeft || x1InRight);
x2InLeft = IsInTree(root->_left, x2);
x2InRight = IsInTree(root->_right, x2);
assert(x2InLeft || x2InRight);
if ((x1InLeft && x2InRight) || (x1InRight && x2InLeft))
return root;
else if (x1InLeft&&x2InLeft)
return GetLastestCommonAncestor2(root->_left, x1, x2);
else
return GetLastestCommonAncestor2(root->_right, x2, x2);
}
//普通二叉树(三叉链方法的思想)
bool GetNodePaths(Node* root, Node* x, stack<Node*>& s)
{
if (root == NULL)
return;
s.push(root);
if (root == x)
return true;
bool inLeft = GetNodePaths(root->_left, x, s);
if (inLeft)
return true;
bool inRight = GetNodePaths(root->_right, x, s);
if (inRight)
return true;
s.pop();
return false;
}
Node* GetLastestCommonAncestor3(Node* root, Node* x1, Node* x2)
{
assert(x1&&x2);
if (root == NULL)
return NULL;
stack<Node*> paths1, paths2;
if (!GetNodePaths(root, x1, paths1) || !GetNodePaths(root, x2, paths2))
return NULL;
while (paths1.size() != paths2.size())
{
if (paths1.size() > paths2.size())
paths1.pop();
else
paths2.pop();
}
while (!paths1.empty() && !paths2.empty() && paths1.top() != paths2.top())
{
paths1.pop();
paths2.pop();
}
if (paths1.top() == paths2.top())
return paths1.top();
return NULL;
}