题目:二叉树的结点定义如下:

 

1 struct TreeNode
2 {
3     int m_nvalue;
4     TreeNode* m_pLeft;
5     TreeNode* m_pRight;
6 };

 

输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点。

 

分析:求数中两个结点的最低共同结点是面试中经常出现的一个问题。这个问题至少有两个变种。

 

第一变种是二叉树是一种特殊的二叉树:查找二叉树。也就是树是排序过的,位于左子树上的结点都比父结点小,而位于右子树的结点都比父结点大。我们只需要从根结点开始和两个结点进行比较。如果当前结点的值比两个结点都大,则最低的共同父结点一定在当前结点的左子树中。如果当前结点的值比两个结点都小,则最低的共同父结点一定在当前结点的右子树中。

 

第二个变种是树不一定是二叉树,每个结点都有一个指针指向它的父结点。于是我们可以从任何一个结点出发,得到一个到达树根结点的单向链表。因此这个问题转换为两个单向链表的第一个公共结点。我们在本面试题系列的第35题讨论了这个问题。

前面我们提过如果结点中有一个指向父结点的指针,我们可以把问题转化为求两个链表的共同结点。现在我们可以想办法得到这个链表。我们在本面试题系列的第4题中分析过如何得到一条中根结点开始的路径。我们在这里稍作变化即可:

 

 1 /////////////////////////////////////////////////////////////////////////////////
 2 // Get the path form pHead and pNode in a tree with head pHead
 3 /////////////////////////////////////////////////////////////////////////////////
 4 bool GetNodePath(TreeNode* pHead, TreeNode* pNode, std::list<TreeNode*>& path)
 5 {
 6     if(pHead == pNode)
 7         return true;
 8  
 9     path.push_back(pHead);
10  
11     bool found = false;
12     if(pHead->m_pLeft != NULL)
13         found = GetNodePath(pHead->m_pLeft, pNode, path);
14     if(!found && pHead->m_pRight)
15         found = GetNodePath(pHead->m_pRight, pNode, path);
16  
17     if(!found)
18         path.pop_back();
19  
20     return found;
21 }

 

由于这个路径是从跟结点开始的。最低的共同父结点就是路径中的最后一个共同结点:

 

 1 /////////////////////////////////////////////////////////////////////////////////
 2 // Get the last common Node in two lists: path1 and path2
 3 /////////////////////////////////////////////////////////////////////////////////
 4 TreeNode* LastCommonNode
 5 (
 6     const std::list<TreeNode*>& path1,
 7     const std::list<TreeNode*>& path2
 8 )
 9 {
10     std::list<TreeNode*>::const_iterator iterator1 = path1.begin();
11     std::list<TreeNode*>::const_iterator iterator2 = path2.begin();
12    
13     TreeNode* pLast = NULL;
14  
15     while(iterator1 != path1.end() && iterator2 != path2.end())
16     {
17         if(*iterator1 == *iterator2)
18             pLast = *iterator1;
19  
20         iterator1++;
21         iterator2++;
22     }
23  
24     return pLast;
25 }

 

有了前面两个子函数之后,求两个结点的最低共同父结点就很容易了。我们先求出从根结点出发到两个结点的两条路径,再求出两条路径的最后一个共同结点。代码如下:

 

 1 /////////////////////////////////////////////////////////////////////////////////
 2 // Find the last parent of pNode1 and pNode2 in a tree with head pHead
 3 /////////////////////////////////////////////////////////////////////////////////
 4 TreeNode* LastCommonParent_2(TreeNode* pHead, TreeNode* pNode1, TreeNode* pNode2)
 5 {
 6     if(pHead == NULL || pNode1 == NULL || pNode2 == NULL)
 7         return NULL;
 8  
 9     std::list<TreeNode*> path1;
10     GetNodePath(pHead, pNode1, path1);
11  
12     std::list<TreeNode*> path2;
13     GetNodePath(pHead, pNode2, path2);
14  
15     return LastCommonNode(path1, path2);
16 }

 

这种思路的时间复杂度是O(n),时间效率要比第一种方法好很多。但同时我们也要注意到,这种思路需要两个链表来保存路径,空间效率比较低。

以上转自何海涛博客

要求两节点的最近共同父节点(LCA,lowest common ancestor),可以采用树的后序遍历。如果这两个节点不在一条线上,则它们必定分别在所求节点A的左子树和右子树上,后序遍历到第一个满足这个条件的节点就是所要求的节点A。另外,当这两个节点在一条线上,所求节点A则是这两个节点中层次最低的节点的父节点。

 

 1 static bool lca(Node *root, int va, int vb, Node *&result, Node* parrent)
 2 {
 3   // left/right 左/右子树是否含有要判断的两节点之一 
 4   bool left = false, right = false;
 5   if (!result && root->left) left = lca(root->left,va,vb,result,root);
 6   if (!result && root->right) right = lca(root->right,va,vb,result,root);
 7   // mid 当前节点是否是要判断的两节点之一 
 8   bool mid = false;
 9   if (root->data == va || root->data == vb) mid=true;
10   if (!result && int(left + right + mid) == 2) {
11     if (mid) result = parrent;
12     else result = root;
13   }
14   return left | mid | right ;
15 }
16 
17 Node *lca(Node *root,int va, int vb)
18 {
19   if (root == NULL) return NULL;
20   Node *result = NULL;
21   lca(root, va, vb,result, NULL);
22   return result;
23 }

 

以上转自flyinghearts

 

 

 

 

 

 

 

 

相关文章: