说实话,和这题很有缘分。首先,这个题目给我印象很深,其次,今天在做蘑菇街实习生笔试时遇到了这个题目,所以感觉应该写博客,把这个题目记录下来。
笔试时遇到的题目是:给定二叉树中的两个结点,寻找最低公共节点。
如果树的节点有指向父节点的指针
这是一种情况,是比较简单的情况。
这种情况就相当于求两条链表的公共节点。求解很简单,先把遍历把两条链表的长度求得。加入list1长a,list2长为b,求则a、b中的较大者先从头遍历走a-b步,然后两条链表同时遍历,第一个相同的节点是最低公父节点。
如果树是普通二叉树
这种情况难点。所谓两个结点的公共祖先,指的是两个结点都出现在现在某个节点的子树中。我们可以从根结点开始遍历树,每遍历到一个结点时,判断两个输入结点是不是在它的子树中。如果在子树中,则分别遍历它的所有子结点,并判断两个输入结点是不是在它们的子树中。这样一直向下找,直到找到一个结点,它自己的子树同时包含两个输入的子结点而它的子结点却没有,那么该结点就是最低的公共祖先。
我在这次笔试就是采用这种解法。
typedef struct treeNode {
struct treeNode *left;
struct treeNode *right;
int val;
} TreeNode;
/* 查看root结点的子树是否包含distri */
bool hasNode(TreeNode *root, TreeNode *distri){
if (root == NULL){
return false;
}
if (root->val == distri->val){
return true;
}
return hasNode(root->left, distri) || hasNode(root->right, distri);
}
TreeNode *commomFather(TreeNode *root, TreeNode *node1, TreeNode *node2){
/* 如果root的左右结点没有都包含两个给定的结点 */
if ( !((hasNode(root->left, node1)&&hasNode(root->left, node2))
&& (hasNode(root->right, node1) && hasNode(root->right, node2)))){
return root;
}else if(NULL != commomFather(root->left, node1, node2)){
return root->left;
}else if ((NULL != commomFather(root->left, node1, node2)){
return root->right;
}
return NULL;
}
当然我并没有检验,这是凭记忆回想自己在考场写的代码。
更好的解法
上一种方法很明显,我们对同一结点都会重复遍历很多次,我们寻求一种更快的解法。
《剑指offer》提供了一种思路:
使用辅助内存,用两个链表分别保存从根结点到输入两个结点的路径,然后把问题转换为两个链表的最后公共结点。
这种解法为了得到根结点开始到输入的两个结点的两条路径,需要两次遍历树,每一次遍历需要O(n).得到两条路径长度最差是O(n),通常情况下两条路径长度是O(logn).
/* 获取root到node的path */
bool GetNodePath(TreeNode *root, TreeNode *node, list<TreeNode *> &path){
/* 迭代终止条件 */
if (root->val == node->val){
return true;
}
path.push_back(root);
bool found = false;
vector<TreeNode *>::iterator it = root->m_vChildren.begin();
while (!found && i < root->m_vChildren.end()){
found = GetNodePath(*i, node, path);
++i;
}
if (!found){
path.pop_back();
}
return found;
}
/* 获取相同结点 */
TreeNode *GetLastCommonNode(const list<TreeNode *> &path1, const list<TreeNode *> &path2){
list<TreeNode *>::const_iterator iterator1 = path1.begin();
list<TreeNode *>::const_iterator iterator2 = path2.begin();
TreeNode *pLast = NULL;
while (iterator1 != path1.end() && iterator2 != path2.end()){
if (*iterator1 == *iterator2){
pLast = *iterator1;
}
iterator1++;
iterator2++;
}
return pLast;
}
TreeNode *GetLastCommonParent(TreeNode *root, TreeNode *node1, TreeNode *node2){
if (root == NULL || node1 == NULL || node2 == NULL){
return NULL;
}
list<TreeNode *> path1;
GetNodePath(root, node1, path1);
list<TreeNode *> path2;
GetNodePath(root, node2, path2);
return GetLastCommonNode(path1, path2);
}
好了,完