题目链接地址:
题目描述:
给定一棵树,同时给出树中的两个结点,求它们的最低公共祖先。
输入:
输入可能包含多个测试样例。
对于每个测试案例,输入的第一行为一个数n(0<n<1000),代表测试样例的个数。
其中每个测试样例包括两行,第一行为一个二叉树的先序遍历序列,其中左右子树若为空则用0代替,其中二叉树的结点个数node_num<10000。
第二行为树中的两个结点的值m1与m2(0<m1,m2<10000)。
输出:
对应每个测试案例,
输出给定的树中两个结点的最低公共祖先结点的值,若两个给定结点无最低公共祖先,则输出“My God”。
样例输入:
2
1 2 4 6 0 0 7 0 0 5 8 0 0 9 0 0 3 0 0
6 8
1 2 4 6 0 0 7 0 0 5 8 0 0 9 0 0 3 0 0
6 12
样例输出:
2
My God
解题思路:
寻找树中两个结点的最低公共祖先,我的想法是对树中的每个结点都增加一个指向其父结点的指针,每个结点到二叉树根结点的路径就会形成一个链表,这样就把寻找树中两个结点的最低公共祖先转化成了寻找两个链表中的第一个公共结点。
例如对于测试用例:
1 2 4 6 0 0 7 0 0 5 8 0 0 9 0 0 3 0 0
6 8
根据先序遍历构造的二叉树如图1所示:
图1 根据先序遍历构造的二叉树
由图1可以得知,结点6到根结点1的路径所构成的链表是:
6 --> 4 --> 2 --> 1 ①
结点8到根结点1的路径所构成的链表是:
8 --> 5 --> 2 --> 1 ②
接下来从头至尾依次遍历链表①中的每个结点node1,判断node1是否存在于链表②中。如果node1存在于链表②中,则表示node1是这两个链表的第一个公共结点;否则继续遍历链表①的下一个结点,直至链表①中的结点全部遍历完为止。可以得知链表①和链表②的第一个公共结点是2,因此2是结点6和结点8的最低公共祖先。这种算法是从两个结点开始,从下往上查找这两个结点的最低公共祖先。但是这种算法没有通过第4组测试数据,WA了。。。
后来上作者的博客 程序员面试题精选100题(48)-二叉树两结点的最低共同父结点[数据结构] 看了一下,发现了一种从根结点开始,从上向下查找树中两个结点的最低公共祖先的算法,我是采用这种算法AC的,下面简单介绍一下该算法:
(1)先判断结点m1和m2是否都在二叉树中,如果m1和m2都在二叉树中,则进入步骤(2),否则可以认为这两个结点不具有最低公共祖先;
(2)从树的根结点root开始,分以下4种情况进行讨论:
1) 如果根结点root与m1和m2都不相等则进入步骤2),
否则分以下两种情况讨论:
如果根结点root等于结点m1,则说明m1是m1和m2的最低公共祖先,
如果根结点root等于结点m2,则说明m2是m1和m2的最低公共祖先;
2) 如果m1和m2分别位于根结点的左右两棵子树中,则可以认为根结点root就是m1和m2的最低公共祖先;
3) 如果m1和m2都位于根结点的左子树中,则说明m1和m2的最低公共祖先存在于根结点的左子树中,
此时可继续递归查找根结点的左子树,直至找到m1和m2的最低公共祖先为止;
4) 如果m1和m2都位于根结点的右子树中,则说明m1和m2的最低公共祖先存在于根结点的右子树中,
此时可继续递归查找根结点的右子树,直至找到m1和m2的最低公共祖先为止。
“从两个结点开始,从下往上查找这两个结点的最低公共祖先”与“从根结点开始,从上向下查找树中两个结点的最低公共祖先”这两种算法,在大多数测试用例上运行时所得到的结果是一样的,但是当树中出现重复的结点时,这两种算法的运行结果可能就不相同了,具体查看图2:
图2 存在重复结点的二叉树
对于图2中的二叉树,如果要查找结点2和结点2的最低公共祖先,从下往上查找得到结果是2,从上向下查找的结果是1。我不知道从下往上查找算法WA,是不是因为测试数据中包含了这组测试数据。这是我根据AC代码生成的一些测试数据:题目1509:树中两个结点的最低公共祖先的测试数据
AC代码如下:
#include<stdio.h>
#include<malloc.h>
#include<vector>
using namespace std;
// 定义二叉树的结点
typedef struct Node
{
int data; // 数据域
Node * lChild; // 左孩子
Node * rChild; // 右孩子
}BinaryTreeNode;
vector <int> preOrderSequence; // 用于保存二叉树的先序遍历序列
/**
* 根据先序遍历序列构造二叉树
* @return 所构二叉树的根结点
*/
BinaryTreeNode * createBinaryTreeByPreOrder()
{
int data;
BinaryTreeNode * root = NULL;
scanf("%d",&data);
if(0 != data)
{
preOrderSequence.push_back(data);
root = (BinaryTreeNode *)malloc(sizeof(BinaryTreeNode));
root -> data = data;
root -> lChild = createBinaryTreeByPreOrder(); // 递归构造左子树
root -> rChild = createBinaryTreeByPreOrder(); // 递归构造右子树
}
return root;
}
/**
* 从根结点从上自下寻找两个结点的最低公共祖先
* @param BianaryTreeNode * root 二叉树的根结点
* @param int m1 输入的结点m1
* @param int m2 输入的结点m2
* @return 返回m1和m2最低公共祖先结点,如果二者不存在最低公共祖先结点,则返回NULL
*/
BinaryTreeNode * findLatestCommonAncestor(BinaryTreeNode * root,int m1,int m2)
{
if(NULL == root)
return NULL;
else
{
BinaryTreeNode * commonAncestor = NULL; // commonAncestor指向m1和m2的最低公共祖先结点
int rootNodeData = root -> data; // 当前二叉树根结点的值
// 如果m1是m2的祖先结点,则二者的最低公共祖先是m1;如果m2是m1的祖先结点,则二者的最低公共祖先是m2。
if(rootNodeData == m1 || rootNodeData == m2)
{
commonAncestor = root;
}
else
{
//lChildCommonAncestor表示最低公共祖先出现在左子树中,rChildCommonAncestor表示最低公共祖先出现在右子树中
BinaryTreeNode * lChildCommonAncestor = findLatestCommonAncestor(root -> lChild,m1,m2);
BinaryTreeNode * rChildCommonAncestor = findLatestCommonAncestor(root -> rChild,m1,m2);
//如果lChildCommonAncestor和rChildCommonAncestor都不为NULL
//则说明m1,m2分别出现在root的左右子树中,此时root为m1和m2的最低公共祖先结点
if(NULL != lChildCommonAncestor && NULL != rChildCommonAncestor)
{
commonAncestor = root;
}
else
{
if(NULL != lChildCommonAncestor) // m1和m2都出现在root的左子树中,lChildCommonAncestor是它们的最低公共祖先结点
{
commonAncestor = lChildCommonAncestor;
}
if(NULL != rChildCommonAncestor) // m1和m2都出现在root的右子树中,rChildCommonAncestor是它们的最低公共祖先结点
{
commonAncestor = rChildCommonAncestor;
}
}
}
return commonAncestor;
}
}
/**
* 通过后序遍历释放二叉树中的所有结点
* @param BinaryTreeNode * root 二叉树的根结点
* @return void
*/
void deleteBinaryTreeByPostOrder(BinaryTreeNode * root)
{
if(NULL == root)
return;
else
{
deleteBinaryTreeByPostOrder(root -> lChild);
deleteBinaryTreeByPostOrder(root -> rChild);
//printf("%d ",root -> data);
free(root);
root = NULL;
}
}
int main()
{
BinaryTreeNode * root; // 二叉树的根结点
BinaryTreeNode * commonAncestor; // 最低公共祖先结点
int i,n;
int m1,m2;
bool isM1inBinaryTree,isM2inBinaryTree;
scanf("%d",&n);
while(n--)
{
preOrderSequence.clear();
root = createBinaryTreeByPreOrder();
scanf("%d%d",&m1,&m2);
isM1inBinaryTree = false;
isM2inBinaryTree = false;
// 判断m1和m2是否都在二叉树中
for(i = 0;i < preOrderSequence.size();i++)
{
if(preOrderSequence[i] == m1)
{
isM1inBinaryTree = true;
}
if(preOrderSequence[i] == m2)
{
isM2inBinaryTree = true;
}
}
if(isM1inBinaryTree && isM2inBinaryTree)
{
commonAncestor = findLatestCommonAncestor(root,m1,m2);
if(NULL != commonAncestor)
{
printf("%d\n",commonAncestor -> data);
}
else
{
printf("My God\n");
}
}
else
{
printf("My God\n");
}
deleteBinaryTreeByPostOrder(root);
}
return 0;
}
/**************************************************************
Problem: 1509
User: blueshell
Language: C++
Result: Accepted
Time:130 ms
Memory:1024 kb
****************************************************************/