二叉树中查找两个节点的最低公共祖先

这是一道企业面试中,经常会被问到的面试题目。

在网上看到一些此题的实现,其中有两种方法是比较适合编程的。本项目的源代码,请点击这里下载。

方法一:

  此方法是根据二叉树的DFS查找并标记祖先,根据递归出栈的原理,找到公共祖先。

  其主要代码如下:

 1 #include <iostream>
 2 #include <list>
 3 #include "LinerLCA.h"
 4 
 5 //DFS左右子树,查找pNode是否存在
 6 /*@param pRoot 根节点
 7 /*@param pNode 需要查找的节点
 8 /*@param path pNode所存在的路径
 9 */
10 bool GetNodePath(BinaryTreeNode* pRoot, BinaryTreeNode* pNode, list<BinaryTreeNode*>& path)
11 {
12     if(pRoot == pNode)
13         return true;
14 
15     path.push_back(pRoot);
16 
17     bool found = false;
18 
19     if (pRoot->m_pLeft != NULL)
20     {
21         found = GetNodePath(pRoot->m_pLeft, pNode, path);
22     }
23 
24     if (pRoot->m_pRight != NULL && !found)
25     {
26         found = GetNodePath(pRoot->m_pRight, pNode, path);
27     }
28 
29     if(!found)
30         path.pop_back();
31 
32     return found;
33 }
34 //遍历path1、path2,找到最后面的公共节点
35 BinaryTreeNode* GetLastCommonNode(list<BinaryTreeNode*>& path1, list<BinaryTreeNode*>& path2)
36 {
37     list<BinaryTreeNode*>::const_iterator iterator1 = path1.begin();
38     list<BinaryTreeNode*>::const_iterator iterator2 = path2.begin();
39 
40     BinaryTreeNode* pLast = NULL;
41 
42     while(iterator1 != path1.end() && iterator2 != path2.end() && *iterator1 == *iterator2)
43     {
44         pLast = *iterator1;
45         iterator1++;
46         iterator2++;
47     }
48 
49     return pLast;
50 }
51 //函数入口
52 BinaryTreeNode* GetLastCommonParent(BinaryTreeNode* pRoot, BinaryTreeNode* pNode1, BinaryTreeNode* pNode2)
53 {
54     if(pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
55         return NULL;
56 
57     list<BinaryTreeNode*> path1;
58     GetNodePath(pRoot, pNode1, path1);
59 
60     list<BinaryTreeNode*> path2;
61     GetNodePath(pRoot, pNode2, path2);
62 
63     return GetLastCommonNode(path1, path2);
64 }

方法二:

  此方法是在一本书里看到的,其主要思想是,先遍历二叉树,找到从根到两个节点的两条路径,然后再求解出两条路径的第一个公共交点,为最低共同祖先。

  主要代码如下:

 1 #include "RecursionLCA.h"
 2 
 3 /*递归计算根节点与node1、node2的关系
 4 * @param root 要查找节点的根节点 
 5 * @param node1 节点一
 6 * @param node2 节点二
 7 * @param restult 共同父节点结果的引用
 8 * @param parent 根节点的父节点
 9 */  
10 bool LCA(BinaryTreeNode* root, BinaryTreeNode* node1, BinaryTreeNode* node2, BinaryTreeNode*& restult, BinaryTreeNode* parent)
11 {
12      if (!root)
13      {
14         return false;
15      }
16 
17      bool falg1 = LCA(root->m_pLeft, node1, node2, restult, root);
18      bool falg2 = LCA(root->m_pRight, node1, node2, restult, root);
19      //在节点的两个子树上
20      if (falg1 && falg2)
21      {
22         //只有确定在两棵子树上时,根节点为共同父节点
23         restult = root;
24         return true;
25      }
26      //其中的一个节点是根节点
27      if (root->m_nValue == node1->m_nValue || root->m_nValue == node2->m_nValue)
28      {
29          if (falg1 || falg2)
30          {
31              restult = parent;
32          }
33          return true;
34      }
35      return falg1 || falg2; 
36 }
37 //函数入口
38 BinaryTreeNode* RecurLCA(BinaryTreeNode* root, BinaryTreeNode* node1, BinaryTreeNode* node2)
39 {
40     BinaryTreeNode* restult = NULL;
41     LCA(root, node1, node2, restult, NULL);
42     return restult;
43 }

测试数据如下:

测试结果:

 

  都可通过。

小结:

  方法一:比较直观,在查找节点的同时,还做了标记,一气呵成,想到此方法要求分析能力较强。

  方法二:思想比较简单,逻辑清晰,但空间复杂度较多。

  在时间效率上,两者相差不多,方法一应为只有递归操作,省去了开辟额外空间的开销,效率略高。

  读者如有更好的方法,欢迎交流、指正。

 

posted @ 2012-11-12 19:27  MichaelGD  阅读(560)  评论(0编辑  收藏  举报