这是一道企业面试中,经常会被问到的面试题目。
在网上看到一些此题的实现,其中有两种方法是比较适合编程的。本项目的源代码,请点击这里下载。
方法一:
此方法是根据二叉树的DFS查找并标记祖先,根据递归出栈的原理,找到公共祖先。
其主要代码如下:
#include <iostream>
#include <list>
#include "LinerLCA.h"
//DFS左右子树,查找pNode是否存在
/*@param pRoot 根节点
/*@param pNode 需要查找的节点
/*@param path pNode所存在的路径
*/
bool GetNodePath(BinaryTreeNode* pRoot, BinaryTreeNode* pNode, list<BinaryTreeNode*>& path)
{
if(pRoot == pNode)
return true;
path.push_back(pRoot);
bool found = false;
if (pRoot->m_pLeft != NULL)
{
found = GetNodePath(pRoot->m_pLeft, pNode, path);
}
if (pRoot->m_pRight != NULL && !found)
{
found = GetNodePath(pRoot->m_pRight, pNode, path);
}
if(!found)
path.pop_back();
return found;
}
//遍历path1、path2,找到最后面的公共节点
BinaryTreeNode* GetLastCommonNode(list<BinaryTreeNode*>& path1, list<BinaryTreeNode*>& path2)
{
list<BinaryTreeNode*>::const_iterator iterator1 = path1.begin();
list<BinaryTreeNode*>::const_iterator iterator2 = path2.begin();
BinaryTreeNode* pLast = NULL;
while(iterator1 != path1.end() && iterator2 != path2.end() && *iterator1 == *iterator2)
{
pLast = *iterator1;
iterator1++;
iterator2++;
}
return pLast;
}
//函数入口
BinaryTreeNode* GetLastCommonParent(BinaryTreeNode* pRoot, BinaryTreeNode* pNode1, BinaryTreeNode* pNode2)
{
if(pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
return NULL;
list<BinaryTreeNode*> path1;
GetNodePath(pRoot, pNode1, path1);
list<BinaryTreeNode*> path2;
GetNodePath(pRoot, pNode2, path2);
return GetLastCommonNode(path1, path2);
}
方法二:
此方法是在一本书里看到的,其主要思想是,先遍历二叉树,找到从根到两个节点的两条路径,然后再求解出两条路径的第一个公共交点,为最低共同祖先。
主要代码如下:
#include "RecursionLCA.h"
/*递归计算根节点与node1、node2的关系
* @param root 要查找节点的根节点
* @param node1 节点一
* @param node2 节点二
* @param restult 共同父节点结果的引用
* @param parent 根节点的父节点
*/
bool LCA(BinaryTreeNode* root, BinaryTreeNode* node1, BinaryTreeNode* node2, BinaryTreeNode*& restult, BinaryTreeNode* parent)
{
if (!root)
{
return false;
}
bool falg1 = LCA(root->m_pLeft, node1, node2, restult, root);
bool falg2 = LCA(root->m_pRight, node1, node2, restult, root);
//在节点的两个子树上
if (falg1 && falg2)
{
//只有确定在两棵子树上时,根节点为共同父节点
restult = root;
return true;
}
//其中的一个节点是根节点
if (root->m_nValue == node1->m_nValue || root->m_nValue == node2->m_nValue)
{
if (falg1 || falg2)
{
restult = parent;
}
return true;
}
return falg1 || falg2;
}
//函数入口
BinaryTreeNode* RecurLCA(BinaryTreeNode* root, BinaryTreeNode* node1, BinaryTreeNode* node2)
{
BinaryTreeNode* restult = NULL;
LCA(root, node1, node2, restult, NULL);
return restult;
}
测试数据如下:
测试结果:
小结:
方法一:比较直观,在查找节点的同时,还做了标记,一气呵成,想到此方法要求分析能力较强。
方法二:思想比较简单,逻辑清晰,但空间复杂度较多。
在时间效率上,两者相差不多,方法一应为只有递归操作,省去了开辟额外空间的开销,效率略高。
读者如有更好的方法,欢迎交流、指正。