题目:
输入两个树节点,求它们的最低公共祖先节点。
一、二叉搜索树
应聘者:这棵树是不是二叉树?
面试官:是二叉树,并且是二叉搜索树。
思路:
二叉搜索树是经过排序的,位于左子树的节点都比父节点小,位于右子树的节点都比父节点大。既然要找最低的公共祖先节点,我们可以从根节点开始进行比较。
若当前节点的值比两个节点的值都大,那么最低的祖先节点一定在当前节点的左子树中,则遍历当前节点的左子节点;
反之,若当前节点的值比两个节点的值都小,那么最低的祖先节点一定在当前节点的右子树中,则遍历当前节点的右子节点;
这样,直到找到一个节点,位于两个节点值的中间,则找到了最低的公共祖先节点。
struct TreeNode
{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr){}
};
TreeNode* GetLastCommonParent(TreeNode* pRoot, TreeNode* pNode1, TreeNode* pNode2)
{
if (pRoot == nullptr || pNode1 == nullptr || pNode2 == nullptr)
return nullptr;
if (pRoot->val > pNode1->val && pRoot->val > pNode2->val)
return GetLastCommonParent(pRoot->left, pNode1, pNode2);
else if (pRoot->val < pNode1->val && pRoot->val < pNode2->val)
return GetLastCommonParent(pRoot->right, pNode1, pNode2);
else
return pRoot;
}
二、普通的二叉树,树的节点中有指向父节点的指针
面试官:如果只是普通的二叉树呢?
应聘者:树的节点中有没有指向父节点的指针?
面试官:为什么需要指向父节点的指针?
应聘者:如果存在parent指针,则分别从输入的p节点和q节点指向root根节点,其实这就是两个单链表。问题转化为求两个单链表相交的第一个公共节点。
(此题可以用《剑指offer》中面试题52:两个链表的第一个公共节点,求解)
三、普通的二叉树,树的节点中没有指向父节点的指针
面试官:那如果不存在parent指针呢?
应聘者:所谓两个节点的公共祖先,指的是这两个节点都出现在某个节点的子树中。我们可以从根节点开始遍历一棵树,每遍历到一个节点时,判断两个输入节点是不是在它的子树中。如果在子树中,则分别遍历它的所有子节点,并判断两个节点是不是在它们的子树中。这样从上到下一直找到的第一个节点,它自己的子树中同时包含两个输入的节点而它的子节点却没有,那么该节点就是最低的公共祖先。
struct TreeNode
{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr){}
};
bool FindTreeNode(TreeNode* pRoot, TreeNode* pNode)
{
if (pRoot == nullptr || pNode == nullptr)
return false;
if (pRoot == pNode)
return true;
bool found = FindTreeNode(pRoot->left, pNode);
if (!found)
return FindTreeNode(pRoot->right, pNode);
return found;
}
TreeNode* GetLastCommonParent(TreeNode* pRoot, TreeNode* pNode1, TreeNode* pNode2)
{
if (pRoot == nullptr || pNode1 == nullptr || pNode2 == nullptr)
return nullptr;
if (FindTreeNode(pRoot->left, pNode1))
{
if (FindTreeNode(pRoot->right, pNode2))
return pRoot;
else
return GetLastCommonParent(pRoot->left, pNode1, pNode2);
}
else
{
if (FindTreeNode(pRoot->left, pNode2))
return pRoot;
else
return GetLastCommonParent(pRoot->right, pNode1, pNode2);
}
}
面试官:听起来不错。很明显,这种思路会对同一节点重复遍历很多次。你想想看还有没有更快的算法?
应聘者:我的想法是用两个链表分别保存从跟节点到输入的两个节点的路径,然后把问题转换成两个链表的最后公共节点。
struct TreeNode
{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr){}
};
bool GetNodePath(TreeNode* pRoot, TreeNode* pNode, vector<TreeNode* > &path)
{
if (pRoot == nullptr)
return false;
path.push_back(pRoot);
if (pRoot == pNode)
return true;
bool found = false;
found = GetNodePath(pRoot->left, pNode, path);
if (!found)
found = GetNodePath(pRoot->right, pNode, path);
if (!found)
path.pop_back();
return found;
}
TreeNode* GetLastCommonNode(const vector<TreeNode* > &path1, const vector<TreeNode* > &path2)
{
vector<TreeNode* >::const_iterator ite1 = path1.begin();
vector<TreeNode* >::const_iterator ite2 = path2.begin();
TreeNode* pLast = nullptr;
while (ite1 != path1.end() && ite2 != path2.end())
{
if (*ite1 == *ite2)
pLast = *ite1;
else
break;
++ite1;
++ite2;
}
return pLast;
}
TreeNode* GetLastCommonParent(TreeNode* pRoot, TreeNode* pNode1, TreeNode* pNode2)
{
if (pRoot == nullptr || pNode1 == nullptr || pNode2 == nullptr)
return nullptr;
vector<TreeNode* > path1;
vector<TreeNode* > path2;
GetNodePath(pRoot, pNode1, path1);
GetNodePath(pRoot, pNode2, path2);
return GetLastCommonNode(path1, path2);
}
四、普通的树
(不知道树有多少个子树的情况)
struct TreeNode
{
int val;
vector<TreeNode* > children;//由于是普通树,不知有多少个子树,所以用vector保存子树
TreeNode(int x) : val(x){}
};
bool GetNodePath(TreeNode* pRoot, TreeNode* pNode, vector<TreeNode* > &path)
{
if(pRoot == nullptr)
return false;
path.push_back(pRoot);
if (pRoot == pNode)
return true;
bool found = false;
vector<TreeNode* >::iterator ite = pRoot->children.begin();
while (!found && ite < pRoot->children.end())
{
found = GetNodePath(*ite, pNode, path);
++ite;
}
if (!found)
path.pop_back();
return found;
}
TreeNode* GetLastCommonNode(const vector<TreeNode* > &path1, const vector<TreeNode* > &path2)
{
vector<TreeNode* >::const_iterator ite1 = path1.begin();
vector<TreeNode* >::const_iterator ite2 = path2.begin();
TreeNode* pLast = nullptr;
while (ite1 != path1.end() && ite2 != path2.end())
{
if (*ite1 == *ite2)
pLast = *ite1;
else
break;
++ite1;
++ite2;
}
return pLast;
}
TreeNode* GetLastCommonParent(TreeNode* pRoot, TreeNode* pNode1, TreeNode* pNode2)
{
if (pRoot == nullptr || pNode1 == nullptr || pNode2 == nullptr)
return nullptr;
vector<TreeNode* > path1;
vector<TreeNode* > path2;
GetNodePath(pRoot, pNode1, path1);
GetNodePath(pRoot, pNode2, path2);
return GetLastCommonNode(path1, path2);
}