问题:在一棵二叉树中,给定两个节点,求这两个节点的最低的公共祖先节点,如下图中的,节点 6 和 节点 9 的最低公共祖先节点是节点 5.
最容易联想到的是,这个问题似乎与公共子串的问题有关系,如果我们能求出两个节点到根节点的路径,再使用匹配算法得到公共的路径,取这个路径上最后一个节点,即是所求的最低公共祖先节点。
哈夫曼编码启迪了我,我打算使用 0 表示向左走,1 表示向右走。如,101 表示自根节点,走到右孩子 A,再走到 A 的左孩子 B,再走到 B 的右孩子 C 。于是,根节点到节点 C 的路径便可以使用整数 101 ,即 5 表示,基于这个思路,我们只需要用一个整数即可表示根节点到任何一个节点的路径(如果这个路径大于 32 ,我们可以考虑使用大整数,或者模拟一个大整数)。
想到了这一层,就没有什么难度了,我们只需要针对两个节点,分别求出根节点(如果题目要求的是自根节点开始算起)到这两个节点的路径,再求公共路径上最后一个节点即可。
给出源代码。
#include
#include
typedef unsigned char bool;
typedef unsigned char byte;
#define true 1
#define false 0
typedef struct TREE_NODE
{
struct TREE_NODE *left,*right;
int value;
}node,*tree,*pNode;
#define FOUNDNONE -1
#define FOUNDLEFT 0
#define FOUNDRIGHT 1
/************************************************************************/
/*
获得从根节点到 aNode 的路径。
path 的二进制位指示了该路径,若某位上为 0 ,则表示向左,为 1 则表示向右
level 表示 path 表示的路径的长度。
如 path = 0000101,level = 5,则 path 中实际表示路径的是 00101。
*/
/************************************************************************/
bool getPath(tree theTree,pNode aNode,unsigned int *path,unsigned int *level)
{
if(NULL == theTree)
{
return false;
}
else if(aNode == theTree)
{
return true;
}
else
{
*path = (*path) << 1 | FOUNDLEFT;
++ (*level);
if(getPath(theTree->left,aNode,path,level))
{
return true;
}
else
{
*path = (*path) >> 1;
-- (*level);
*path = (*path) << 1 | FOUNDRIGHT;
++ (*level);
if(getPath(theTree->right,aNode,path,level))
{
return true;
}
else
{
*path = (*path) >> 1;
-- (*level);
return false;
}
}
}
}
/************************************************************************/
/*
求两个二进制都是 totalBits 位的整数 a 和 b 它们的自高位向低位的公共位。
返回公共位表示的整数,level 储存公共位的位长度。
如 a = 1101,b = 1110,则返回 11 ,level = 2
*/
/************************************************************************/
unsigned int commonBit(int a,int b,unsigned int totalBits,unsigned int *level)
{
int mb = 0;
while ((a >>= mb) != (b >>= mb))
{
++ mb;
}
*level = totalBits - mb;
return a;
}
/************************************************************************/
/*
由一个整数表示的路径求得对应的节点
*/
/************************************************************************/
pNode getNode(tree theTree,unsigned int path,unsigned int level)
{
pNode curNode = theTree;
while(-1 != -- level)
{
curNode = (FOUNDLEFT == ((path >> level) & 1)) ? curNode->left : curNode->right;
}
return curNode;
}
/************************************************************************/
/*
最低公共祖先节点
*/
/************************************************************************/
pNode lowestCommonAncestor(tree theTree,pNode aNode,pNode bNode)
{
pNode pCurNode;
unsigned int pathA = 0,levelA = 0,pathB = 0,levelB = 0;
unsigned int commonPath = 0,level = 0;
pCurNode = aNode;
pathA = levelA = 0;
getPath(theTree,pCurNode,&pathA,&levelA);
pCurNode = bNode;
pathB = levelB = 0;
getPath(theTree,pCurNode,&pathB,&levelB);
if(levelA > levelB)
{
pathA >>= (levelA - levelB);
levelA = levelB;
}
else if(levelA < levelB)
{
pathB >>= (levelB - levelA);
levelB = levelA;
}
else
{
//do nothing
}
commonPath = commonBit(pathA,pathB,levelA,&level);
return getNode(theTree,commonPath,level);
}
/************************************************************************/
/*
打印一个整数表示的路径
*/
/************************************************************************/
void printBinary(unsigned int a,unsigned int maxBits)
{
while (maxBits --)
{
printf("%d -> ",(a >> maxBits) & 1);
}
printf("\b\b\b\b");
printf(" \r\n");
}
int main()
{
unsigned int directionA = 0,levelA = 0,directionB = 0,levelB = 0;
node n1,n2,n3,n4,n5,n6,n7,n8,n9;
pNode pLowestAncestNode;
n1.value = 1;n1.left = &n2;n1.right = &n3;
n2.value = 2;n2.left = n2.right = NULL;
n3.value = 3;n3.left = &n4;n3.right = &n5;
n4.value = 4;n4.left = n4.right = NULL;
n5.value = 5;n5.left = &n6;n5.right = &n7;
n6.value = 6;n6.left = &n8;n6.right = NULL;
n7.value = 7;n7.left = &n9;n7.right = NULL;
n8.value = 8;n8.left = n8.right = NULL;
n9.value = 9;n9.left = n9.right = NULL;
pLowestAncestNode = lowestCommonAncestor(&n1,&n4,&n7);
printf("lowest common ancestnode : %d\r\n",pLowestAncestNode->value);
pLowestAncestNode = lowestCommonAncestor(&n1,&n1,&n7);
printf("lowest common ancestnode : %d\r\n",pLowestAncestNode->value);
pLowestAncestNode = lowestCommonAncestor(&n1,&n2,&n8);
printf("lowest common ancestnode : %d\r\n",pLowestAncestNode->value);
pLowestAncestNode = lowestCommonAncestor(&n1,&n3,&n9);
printf("lowest common ancestnode : %d\r\n",pLowestAncestNode->value);
pLowestAncestNode = lowestCommonAncestor(&n1,&n6,&n8);
printf("lowest common ancestnode : %d\r\n",pLowestAncestNode->value);
system("PAUSE");
return 0;
}
该题来源:
何海涛博客:http://zhedahht.blog.163.com/blog/static/25411174201081263815813/
该作者在文章中提到了两种解法,一种是递归的,如果两个节点都在当前节点的左子树,或者右子树中,那么,它们的最低公共祖先节点必然在左子树或者右子树中。如果分散在两个子树中,那么,当前节点就是最低公共祖先节点。
另一种与本文解法类似,只不过空间复杂度要高于本文解法。