这是一道很经典的问题,《剑指Offer》上给出的解法是先找到从根节点到这两个节点的简单路径,这两条简单路径的最后公共节点就是这两个节点的最低公共祖先节点。本文给出的解法不必找到从根节点到这两个节点的简单路径,而且还可以很容易地判断这两个节点是否在这棵二叉树上。
结合代码讲解,代码如下:
/*求二叉树的两个节点的最低公共祖先节点*/
int FindLowestCommonAncestor(PBTNode proot,PBTNode* ppnode,PBTNode pn_arr[2],int* pi)
{/**ppnode=NULL*/
int iret=-1;
if(proot!=NULL&&ppnode!=NULL&&pi!=NULL&&(*pi>=0&&*pi<=2))
{
if(*pi==2)
{
if(proot==pn_arr[0]||proot==pn_arr[1])
{
*pi=(proot==pn_arr[0]?1:0);
iret=Find2(proot->m_left,ppnode,pn_arr,pi);
iret=Find2(proot->m_right,ppnode,pn_arr,pi);
if(*pi<0)
*ppnode=proot;
}
else
{
int i,j;
iret=Find2(proot->m_left,ppnode,pn_arr,pi);
i=*pi;
iret=Find2(proot->m_right,ppnode,pn_arr,pi);
j=*pi;
if((i==0||i==1)&&j<0)
*ppnode=proot;
}
}
else if(*pi==0||*pi==1)
{
if(proot==pn_arr[*pi])
*pi=-1;
else
{
iret=Find2(proot->m_left,ppnode,pn_arr,pi);
iret=Find2(proot->m_right,ppnode,pn_arr,pi);
}
}
iret=0;
}
return iret;
}
BTNode 是二叉树节点的数据类型。
typedef BTNode* PBTNode;
函数FindLowestCommonAncestor()的参数proot表示指向二叉树根节点的指针,ppnode表示一个指向PBTNode变量的指针变量(pnode),如果两个节点都在这棵二叉树上,pnode用于存放找到的两个节点的最低公共祖先节点的内存地址。
PBTNode pn_arr[2],存放给定的两个节点的内存地址。
int* pi,指向一个int变量(tag),调用函数之前*pi被赋值为2,表示还有两个节点未被找到。当找到pn_arr[0]指向的节点,*pi=1,表示还需查找pn_arr[1]指向的节点,当找到pn_arr[1]指向的节点,*pi=0,表示还需查找pn_arr[0]指向的节点。当两个节点都被找到,*pi=-1,表示指定的两个节点都被找到了。所以在函数FindLowestCommonAncestor()返回后,根据*pi的值可以判断目前的查找情况。当*pi==-1时,说明两个节点都被找到了,*ppnode指向两个节点的最低公共祖先节点。
进入函数后首先检查参数的合法性、合理性:
(proot!=NULL&&ppnode!=NULL&&pi!=NULL&&(*pi>=0&&*pi<=2)
分两大类处理:
(一)、如果*pi==2,说明这两个节点都未被找到。检查proot是否指向所要找的节点。
再据此分两类处理:
(1)、若(proot==pn_arr[0]||proot==pn_arr[1])成立,说明找到了一个节点,修改*pi的值:
*pi=(proot==pn_arr[0]?1:0);
再分别在proot的左子树和右子树里查找:
FindLowestCommonAncestor(proot->m_left,ppnode,pn_arr,pi);
FindLowestCommonAncestor(proot->m_right,ppnode,pn_arr,pi);
若*pi<0,说明另一个节点也被找到了,该节点在proot的子树上。那么这两个节点的最低公共祖先节点的地址就是proot的值。
*ppnode=proot;
若*pi>=0,说明另一个节点在不在该二叉树上。
(2)、否则,
分别在proot的左子树和右子树里查找,同时记录每棵子树的查找结果:
int i,j;
FindLowestCommonAncestor(proot->m_left,ppnode,pn_arr,pi);
i=*pi;
FindLowestCommonAncestor(proot->m_right,ppnode,pn_arr,pi);
j=*pi;
若((i==0||i==1)&&j<0)成立,说明这两个节点分别在proot的两棵子树上,proot指向的节点就是这两个节点的最低公共祖先节点:
*ppnode=proot;
(二)、若(*pi==0||*pi==1)成立,则首先检查(proot==pn_arr[*pi])是否成立,若成立,则说明两个节点都找到了,这两个节点的最低公共祖先节点不在proot为根的子树上。
若不成立,分别在proot的左右两棵子树内继续查找。本层函数返回后,直到*pi初为2的那一层,就可以找到最低公共祖先节点了。
本方法的详细实例源码在(https://github.com/wjt2015/BinaryTree160728)处。