面试题50:树中两个结点的最低公共祖先

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:54   1747   0

题目:输入树的两个结点,求它们的最低公共祖先。


第一种情况:当树是二叉树搜索树的时候


思路:根据二叉搜索树的性质,左子树都比父节点小,右子树结点值都比父节点大,从根节点开始便利二叉树,如果所给的两个结点都比当前结点的值小,则遍历当前结点的左子树,如果两个结点的值都比当前结点的值大就遍历当前结点的右子树,直到找出第一个介于两个结点之间的值就是最低公共祖先。

struct BTNode  
{  
    int m_nValue;  
    BTNode* m_pLeft;  
    BTNode* m_pRight;  
}; 

BTNode * FindLCA(BTNode * root,BTNode * node1,BTNode * node2)
{
 if(root==NULL||node1==NULL||node2==NULL)
  return NULL;
 else
 {
  //如果当前结点的值大于两个结点的值,遍历左子树
  if(root->m_nValue>node1->m_nValue && root->m_nValue>node2->m_nValue)
  {
   return FindLCA(root->m_pLeft,node1,node2);
  }//如果当前结点的值小于两个结点的值,遍历右子树
  else if(root->m_nValue<node1->m_nValue && root->m_nValue<node2->m_nValue)
  {
   return FindLCA(root->m_pRight,node1,node2);
  }
  else
  {
   return root;
  }
 }
}

第二种情况:树是一般的二叉树,但是带有指向父节点的指针

思路:如果带有指向父节点的指针,可以将两个结点看着是两个链表的头结点,然后根据求两个链表的第一个公共结点来求。

#include <iostream>
using namespace std;

struct CSNode
{
 int m_nValue;
 CSNode* m_pParent;//指向父节点
 CSNode* m_pFirstChild;//左结点
 CSNode* m_pNextSibling;//右结点
};

//计算链表的长度
int NodeCount(CSNode* pNode)
{
 assert(pNode);
 int nLen = 0;
 while(pNode)
 {
  nLen++;
  pNode = pNode->m_pParent;
 }
 return nLen;
}

//寻找两个链表的公共结点
CSNode* LCA(CSNode*& pFNode,CSNode*& pSNode)
{
 assert(pFNode && pSNode);
 int nDiff = 0;
 int nCountFir = NodeCount(pFNode);
 int nCountSec = NodeCount(pSNode);
 if (nCountFir > nCountSec)
 {
  nDiff = nCountFir - nCountSec;
  while(nDiff)
  {
   pFNode = pFNode->m_pParent;
   nDiff--;
  }
 }
 else
 {
  nDiff = nCountSec - nCountFir;
  while(nDiff)
  {
   pSNode = pSNode->m_pParent;
   nDiff--;
  }
 }
 assert(pSNode && pFNode);
 while(pFNode != pSNode)
 {
  pFNode = pFNode->m_pParent;
  pSNode = pSNode->m_pParent;
 }
 assert(pFNode);
 return pFNode;
}

//传入树的两个结点,查找它们的最低公共祖先
CSNode* FindLCA(CSNode* pRoot,int nFirst,int nSec,CSNode*& pFNode,CSNode*& pSNode)
{
 //先序遍历树,查找这两个结点的位置
 if (!pRoot)
 {
  return NULL;  
 }
 if (pFNode && pSNode)
 {
  return NULL;
 }
 //比较值
 if (pRoot->m_nValue == nFirst)
 {
  pFNode = pRoot;
 }
 if (pRoot->m_nValue == nSec)
 {
  pSNode = pRoot;
 }
 
 if (pFNode && pSNode)
 {
  //两个结点就是两个链表的头结点,查找最低公共祖先
  return LCA(pFNode,pSNode);
 }
 //递归遍历树
 CSNode* pLCALeft = FindLCA(pRoot->m_pFirstChild,nFirst,nSec,pFNode,pSNode);
 CSNode* pLCARight = FindLCA(pRoot->m_pNextSibling,nFirst,nSec,pFNode,pSNode);
 return pLCALeft == NULL ? pLCARight : pLCALeft;
}


代码来源:http://blog.csdn.net/insistgogo/article/details/9934153


第三种情况:既不是二叉搜索树也没有包含指向父节点的指针

思路:首先寻找从根节点到树中某一结点的路径,所以在遍历的时候需要辅助内存来保存路径。

1.首先从根节点开始遍历,遍历A,把A存放到路径中去,路径中只有一个A

2.遍历B,把B也放入路径中,此时路径为A-B

3.遍历D,把D放入路径中,此时路径为A-B-D

4.遍历F,把F存放到路径中,此时路径为A-B-D-F

5.F没有子节点,此路径不可能到达结点H,把F删除,变为A-B-D

6.遍历G,和结点F一样,这条路也不能到达H,所以仍然是A-B-D

7.由于D的所有子节点都已经遍历过,不可能到达H,所以将D也删除,变为A-B

8.遍历E,把E加入路径,变为A-B-E

9.遍历H,到达目标结点,A-B-E就是从根节点到达H所必须经过的路径

10.然后对F求必须经过的路径

11.找出两条路径中最后的公共结点

从根节点开始到输入的两个结点的两条,需要遍历两次树,每遍历一次的时间复杂度是O(n),得到的两条路径的长度在最差情况时是O(n),通常情况下两条路径的长度是O(logn)。


#include <iostream>
#include <vector>
#include <list>
using namespace std;

struct TreeNode 
{
    int m_nValue;    
    std::vector<TreeNode*> m_vChildren;   
};

bool GetNodePath(TreeNode *pRoot,TreeNode *pNode, list<TreeNode*> &path)
{
 if (pRoot=pNode)
 {
  return true;
 }
 path.push_back(pRoot);
 bool found=false;
 vector<TreeNode*>::iterator i=pRoot->m_vChildren.begin();
 while(!found&&i<pRoot->m_vChildren.end())
 {
  found=GetNodePath(*i,pNode,path);
  ++i;
 }
 if (!found)
 {
  path.pop_back();
 }
 return found;
}

TreeNode* GetLastCommonNode(const list<TreeNode*>& path1, const list<TreeNode*>& path2)
{
 list<TreeNode*>::const_iterator i1=path1.begin();
 list<TreeNode*>::const_iterator i2=path2.begin();
 TreeNode* pLast=NULL;
 while(i1!=path1.end()&&i2!=path2.end())
 {
  if (*i1==*i2)
  {
   pLast=*iterator;
  }
  i1++;
  i2++;
 }
 return pLast;
}

TreeNode* GetLastCommonParent(TreeNode* pRoot,TreeNode* pNode1,TreeNode* pNode2)
{
 if (pRoot==NULL||pNode1==NULL||pNode2==NULL)
 {
  return NULL;
 }
 list<TreeNode*> path1;
 GetNodePath(pRoot,pNode1,path1);
 list<TreeNode*> path2;
 GetNodePath(pRoot,pNode2,path2);
 return GetLastCommonNode(path1,path2);
}

代码来自: http://m.blog.csdn.net/blog/dgy610927/39081703

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP