题目:二叉树的结点定义如下:
struct TreeNode
{
int m_nvalue;
TreeNode* m_pLeft;
TreeNode* m_pRight;
};
输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点。
分析:求数中两个结点的最低共同结点是面试中经常出现的一个问题。这个问题至少有两个变种。
第一变种是二叉树是一种特殊的二叉树:查找二叉树。
也就是树是排序过的,位于左子树上的结点都比父结点小,而位于右子树的结点都比父结点大。
/*
第一变种是二叉树是一种特殊的二叉树:查找二叉树。
也就是树是排序过的,位于左子树上的结点都比父结点小,而位于右子树的结点都比父结点大。
*/
/*
二叉排序树插入一个节点
*/
void InsertNode(TreeNode* &root,int value)
{
if(root==NULL)
{
root=new TreeNode(value);
}
else
{
TreeNode *node=new TreeNode(value);
TreeNode *p=root;
while(p)
{
if(value>p->data)
{
if(p->right!=NULL)
p=p->right;
else
{p->right=node;return;}
}
else
{
if(p->left!=NULL)
p=p->left;
else
{p->left=node;return;}
}
}
}
}
//创建一个二叉查找树
void MakeSearchTree(TreeNode* &root)
{
int value=0;
while(cin>>value)
InsertNode(root,value);
}
/*
将数组中的数插入到二叉排序树中
*/
void MakeSearchTree(TreeNode* &root,int p[],int len)
{
for(int i=0;i<len;i++)
InsertNode(root,p[i]);
}
/*
测试生成的二叉排序树
*/
void MakeSearchTreeTest()
{
TreeNode* root=NULL;
cout<<"make search tree "<<endl;
MakeSearchTree(root);
cout<<"the tree : "<<endl;
MidOrderTraverse(root);
cout<<endl;
cout<<"the tree : "<<endl;
LevelOrderTraverse1(root);
cout<<endl;
}
//在二叉查找树中查找等于value的节点。
TreeNode* FindNode(TreeNode* root,int value)
{
if(root==NULL)
return NULL;
while(root!=NULL)
{
if(root->data==value)
return root;
else if(root->data>value)
root=root->left;
else
root=root->right;
}
return NULL;
}
//在二叉查找树中查找node1和node2的公共父节点
TreeNode* CommonParent(TreeNode* root,TreeNode* node1,TreeNode* node2)
{
int value1=node1->data,value2=node2->data;
while(root!=NULL)
{
if((root->data>value1)&&(root->data>value2))
root=root->left;
else if((root->data<value1)&&(root->data<value2))
root=root->right;
else
return root;
}
return NULL;
}
void CommonParentTest()
{
int p[]={8,4,15,2,6,10,19,1,3,5,7,9,17,18,25};
cout<<"nodes : ";
int len=sizeof(p)/sizeof(int);
ShowArray(p,len);
TreeNode* root=NULL;
cout<<"make search tree "<<endl;
MakeSearchTree(root,p,len);
cout<<"the tree : "<<endl;
MidOrderTraverse(root);
cout<<endl;
cout<<"the tree : "<<endl;
LevelOrderTraverse1(root);
cout<<endl;
cin.clear();
int value=0;
cout<<"node1 value : ";
cin>>value;
TreeNode *node1=FindNode(root,value);
cout<<"node2 value : ";
cin>>value;
TreeNode *node2=FindNode(root,value);
cout<<"find common parent : "<<endl;
TreeNode *node=CommonParent(root,node1,node2);
cout<<"common parent : "<<node->data<<endl;
cout<<"parent left chilren : "<<node->left->data<<endl;
cout<<"parent right chilren : "<<node->right->data<<endl;
}
第二个变种是树不一定是二叉树,每个结点都有一个指针指向它的父结点。于是我们可以从任何一个结点出发,
得到一个到达树根结点的单向链表。因此这个问题转换为两个单向链表的第一个公共结点。
如果不是这两种特殊情况,二是一般的二叉树,如何求两个节点的公共父节点?
方法一:
/*
判断node是不是在以root为根节点的树中
*/
bool IsInTree(TreeNode* root,TreeNode* node)
{
if(root==node)
return true;
bool found=false;
if(root->left!=NULL)
found=IsInTree(root->left,node);
if(!found&&root->right)
found=IsInTree(root->right,node);
return found;
}
//求node1和node2的公共父节点
TreeNode* GetCommonParent(TreeNode* root,TreeNode* node1,TreeNode* node2)
{
if(root==NULL)
return NULL;
bool node1left=IsInTree(root->left,node1);
bool node2left=IsInTree(root->left,node2);
bool node1right=IsInTree(root->right,node1);
bool node2right=IsInTree(root->right,node2);
if((node1left&&node2right)||(node1right&&node2left))
return root;
if(node1left&&node2left)
return GetCommonParent(root->left,node1,node2);
if(node1right&&node2right)
return GetCommonParent(root->right,node1,node2);
}
方法二:
//求出root到node的路径,root为根的树中没有node时。返回false
bool GetNodePath(TreeNode* root,TreeNode* node,vector<int> &path)
{
if(root==node)
return true;
path.push_back(root->data);
bool found=false;
if(root->left!=NULL)
found=GetNodePath(root->left,node,path);
if(!found&&root->right!=NULL)
found=GetNodePath(root->right,node,path);
if(!found)
path.pop_back();
return found;
}
//在一个二叉树中找到节点等于value的node节点
TreeNode* FindTreeNode(TreeNode* root,int value)
{
if(root->data==value)
return root;
TreeNode *res=NULL;
if(root->left!=NULL)
res=FindTreeNode(root->left,value);
if(res==NULL&&root->right!=NULL)
res=FindTreeNode(root->right,value);
return res;
}