二叉树中寻找共同节点的最低公共祖先节点

问题:在一棵二叉树中,给定两个节点,求这两个节点的最低的公共祖先节点,如下图中的,节点 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/

该作者在文章中提到了两种解法,一种是递归的,如果两个节点都在当前节点的左子树,或者右子树中,那么,它们的最低公共祖先节点必然在左子树或者右子树中。如果分散在两个子树中,那么,当前节点就是最低公共祖先节点。

另一种与本文解法类似,只不过空间复杂度要高于本文解法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值