求树中的两个节点的最低公共祖先,是一组题目,不同条件下的题目是完全不一样的。
情形1、二叉搜索树
分析:如果树是二叉搜索树的话,就比较容易解决。因为二叉搜索树的特性,左子树的上节点的值比根节点小,右子树上节点的值比根节点大。
思路:从树的根节点开始和两个输入的节点进行比较。
(1)如果当前节点的值比两个节点的值都大,最低公共祖先结点一定在当前结点的左子树。
(2)如果当前节点的值比两个节点的值都小,最低公共祖先节点一定在当前结点的右子树。
(3)如果一个结点比当前结点大或等于,另一个比当前结点小或等于,则最低公共祖先节点为当前结点。
时间复杂度为O(lgN),空间复杂度为O(1)。
Node *GetLastCommomParent(Node *pRoot,Node* p1,Node* p2)
{
if(pRoot == NULL || p1 == NULL || p2 == NULL)
return NULL;
//比根节点都小,到左子树中找
if(p1->data < pRoot->data && p2->data <pRoot->data)
return GetLastCommomParent(pRoot->left,p1,p2);
//比根节点都大,到右子树中找
else if(p1->data > pRoot->data && p2->data > pRoot->data)
return GetLastCommomParent(pRoot->right,p1,p2);
else //其他情况
return pRoot;
}
情形2、普通树,且有指向父节点的指针
如果树中有指向父节点的指针,那么可以把两个节点到根节点的路径想象成两条链表,那么这个问题可以转化为两个链表的第一个公共节点。
举例:求出F和H的最低公共祖先
F->D->B->A
H->E->B->A
这个两个链表的第一个交点B为他们的最低公共祖先。
情形3、普通树,没有指向父节点的指针
方法1、
用两个链表分别保存从根节点到输入的两个节点的路径,然后把问题转换成两个链表的最后公共节点。
时间复杂度:为了得到从根节点开始到输入的两个节点的两条路径,需要遍历两次树,每遍历一次的时间复杂度为O(N),得到两条路径的长度最差的是O(N),通常情况下两条路径是O(lgN)。
注:下面的GetNodePath函数,并没有把最后的节点放入list中
F :A->B->D
H:A->B->E
//找节点的路径,保存到list中
//注:参数path是引用,因为path需要改变里面的内容,
bool GetNodePath(Node* pRoot,Node* pNode,list<Node*>& path)
{
if(pRoot == NULL || pNode == NULL)
return false;
path.push(pRoot);
if(pRoot == pNode)
return true;
bool found = false;
/*
//树的孩子放在一个vector中
vector<Node*>::iterator iter = pRoot->child.begin();
while(!found && iter < pRoot->child.end())
{
found = GetNodePath(*iter, pNode,path);
++iter;
}
if(!found)
path.pop_back();
*/
//为方便测试,可以暂时将树特指为二叉树
found = GetNodePath(pRoot->left,pNode,path);
if(!found)
found = GetNodePath(pRoot->right,pNode,path);
//左右子树都找不到的话,该节点弹出
if(!found)
path.pop_back();
return found;
}
//求两个链表的最后一个公共节点
Node *GetLastCommonNode(const list<Node*>& path1, const list<Node*>& path2)
{
//从后往前找
list<Node*>::const_iterator it1 = path1.end();
list<Node*>::const_iterator it2 = path2.end();
while(--it1 != path1.begin() && --it2 != path2.begin())
{
if(*it1 == *it2)
return *it1;
}
return NULL;
}
Node* GetLastCommonParent(Node* pRoot,Node* pNode1,Node* pNode2)
{
list<Node*> path1;
GetNodePath(pRoot,pNode1,path1);
list<Node*> path2;
GetNodePath(pRoot,pNode2,path2);
return GetLastCommonNode(path1,path2);
}
方法2、
(1)如果两个节点分别在根节点的左子树和右子树,则返回根节点
(2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树。
//判断节点是否在树中
bool FindNode(Node *pRoot,Node *pNode)
{
if (pRoot == NULL || pNode == NULL)
return false;
if (pRoot == pNode)
return true;
bool found = FindNode(pRoot->left,pNode);
if (!found)
return FindNode(pRoot->right,pNode);
return found;
}
//
Node* GetLastCommomParent(Node* pRoot, Node* pNode1, BNode* pNode2)
{
//判断节点pNode1是否在左子树中
if (FindNode(pRoot->_pLeftChild, pNode1))
{
if (FindNode(pRoot->_pRightChild, pNode2))
return pRoot;
else //两个节点都在左子树中
return GetLastCommomParent(pRoot->_pLeftChild,pNode1,pNode2);
}
else //说明pNode1在右子树中
{
if (FindNode(pRoot->_pLeftChild, pNode2))
return pRoot;
else //节点都在右子树中,
return GetLastCommomParent(pRoot->_pRightChild,pNode1,pNode2);
}
}