`
wusuoya
  • 浏览: 644183 次
  • 性别: Icon_minigender_2
  • 来自: 成都
社区版块
存档分类
最新评论

找寻二叉树中两个节点的公共父节点中最近的那个节点

阅读更多

情况1. 节点只有left/right,没有parent指针,root已知

情况2. root未知,但是每个节点都有parent指针

情况3. 二叉树是个二叉查找树,且root和两个节点的值(a, b)已知


虽然情况一是第一个情况,但是看上去比较复杂,我们放到最后来说,先从第二个情况开始说。

                                             10

                                          /       \
                                        6         14
                                      /  \       /   \
                                    4   8   12   16

                                   /  \

                                  3   5

画一个二叉树来做例子。如果我们要找3和8这两个节点的公共父亲节点,我们的做法是首先找到3到根节点的路劲,然后找到8到根节点的路径。

                                             10

                                          //       \
                                        6         14
                                              /   \
                                    4   8   12   16

                                   /   \

                                  3   5

3的路径用红色表示,8的用绿色表示,可以看到, 这里的问题实际上是另一个我们熟知的问题,有2个相交的单链表,找出它们的相交点!

只要把这个二叉树的图片倒过来看,或者把脖子倒过来看就知道了:)那个方法也是传统的求出linkedList A的长度lengthA, linkedList B的长度LengthB。然后让长的那个链表走过abs(lengthA-lengthB)步之后,齐头并进,就能解决了。

  1. int  getLength (bstNode* pNode)  
  2. {     
  3.     int  length = 0;  
  4.     bstNode* pTemp = pNode;  
  5.     while  (pTemp)  
  6.     {  
  7.         length ++ ;  
  8.         pTemp = pTemp->pParent;  
  9.     }  
  10.     return  length;  
  11. }  
  12. bstNode* findLCACase2(bstNode* pNode1, bstNode* pNode2)  
  13. {  
  14.     int  length1 = getLength(pNode1);  
  15.     int  length2 = getLength(pNode2);  
  16.       
  17.     // skip the abs(length1-length2)   
  18.     bstNode* pIter1 = NULL;  
  19.     bstNode* pIter2 = NULL;  
  20.     int  k=0;  
  21.     if  (length1>=length2)  
  22.     {  
  23.         bstNode* pTemp = pNode1;  
  24.         while  (k++<length1-length2)  
  25.         {  
  26.             pTemp = pTemp->pParent;   
  27.         }  
  28.         pIter1 = pTemp;  
  29.         pIter2 = pNode2;  
  30.     }  
  31.     else   
  32.     {  
  33.         bstNode* pTemp = pNode1;  
  34.         while  (k++<length2-length1)  
  35.         {  
  36.             pTemp = pTemp->pParent;   
  37.         }  
  38.         pIter1 = pNode1;  
  39.         pIter2 = pTemp;  
  40.     }  
  41.       
  42.     while  (pIter1&&pIter2&&pIter1!= pIter2)  
  43.     {  
  44.         pIter1 = pIter1->pParent;  
  45.         pIter2 = pIter2->pParent;  
  46.     }  
  47.     return  pIter1;  
  48. }  

自己写了个代码,总觉得有些拖沓冗余,希望有缘人看到文章之后能帮我改写的更和谐一些。

还是原来这个图,情况三,如果是个二叉搜索树,而且root和a, b已知,我们这个case假设a,b=3,8。从知道根这个条件我们很自然联想到递归(当然不递归也可以)地往下找。关键是收敛条件,什么情况下可以判断 出当然检查的这个节点是最近父亲节点呢?其实从这个例子已经可以看出一些端倪了,如果当前访问的节点比a,b来的都小,肯定不行。如果比a,b都大,也不 行。那也就是说,这个节点只有在a<=node<=b的区间内才成立(我们假定a<b这里)。这样的问题,网上广为流传着类似的代码:

  1. bstNode* findLCACase3(bstNode* pNode,  int  value1,  int  value2)  
  2. {  
  3.     bstNode* pTemp = pNode;  
  4.     while  (pTemp)  
  5.     {  
  6.         if  (pTemp->data>value1 && pTemp->data>value2)  
  7.             pTemp = pTemp->pLeft;  
  8.         else   if (pTemp->data<value1 && pTemp->data<value2)  
  9.             pTemp = pTemp->pRight;  
  10.         else   
  11.             return  pTemp;  
  12.     }  
  13.     return  NULL;  
  14. }  

好,前面的问题都解决了,我们再回过头来看第一个情况,只有ROOT和left, right节点,没有parent也不是排序树,怎么办?网络上也流传着很多所谓的LCA,RMQ算法,我们不暇找个最合适的,尤其是在面试的时候,特定 时间空间下你很难写出一个逻辑非常复杂的东西(比如你会在面试的时候去实现一个Suffix Tree还是用动态规划来求最长公共子串,哪怕效率不同,我也选择动态规划:))。所以这里,碰到类似的问题的时候,我选择简单的记录找到node1和 node2的路径,然后再把它们的路径用类似的情况二来做分析,比如还是node1=3,node2=8这个case.我们肯定可以从根节点开始找到3这 个节点,同时记录下路径3,4,6,10,类似的我们也可以找到8,6,10。我们把这样的信息存储到两个vector里面,把长的vector开始的多 余节点3扔掉,从相同剩余长度开始比较,4!=8, 6==6, coooool,我们找到了我们的答案。下面的代码完全按照这个思路写成

  1. #include <vector>   
  2. bool  nodePath (bstNode* pRoot,  int  value, std::vector<bstNode*>& path)  
  3. {  
  4.     if  (pRoot==NULL)  return   false ;  
  5.     if  (pRoot->data!=value)  
  6.     {  
  7.         if  (nodePath(pRoot->pLeft,value,path))  
  8.         {  
  9.             path.push_back(pRoot);  
  10.             return   true ;  
  11.         }  
  12.         else   
  13.         {  
  14.             if  (nodePath(pRoot->pRight,value,path))  
  15.             {  
  16.                 path.push_back(pRoot);  
  17.                 return   true ;  
  18.             }  
  19.             else   
  20.                 return   false ;  
  21.         }  
  22.     }  
  23.     else   
  24.     {  
  25.         path.push_back(pRoot);  
  26.         return   true ;  
  27.     }  
  28. }  
  29. bstNode* findLCACase1(bstNode* pNode, int  value1,  int  value2)  
  30. {  
  31.     std::vector<bstNode*> path1;  
  32.     std::vector<bstNode*> path2;  
  33.     bool  find =  false ;  
  34.     find |= nodePath(pNode, value1, path1);  
  35.     find &= nodePath(pNode, value2, path2);  
  36.     bstNode* pReturn=NULL;  
  37.     if  (find)  
  38.     {  
  39.         int  minSize = path1.size()>path2.size()?path2.size():path1.size();  
  40.         int  it1 = path1.size()-minSize;  
  41.         int  it2 = path2.size()-minSize;  
  42.         for  (;it1<path1.size(),it2<path2.size();it1++,it2++)  
  43.         {  
  44.             if  (path1[it1]==path2[it2])  
  45.             {  
  46.                 pReturn = path1[it1];  
  47.                 break ;  
  48.             }  
  49.         }  
  50.     }  
  51.     return  pReturn;  
  52. }  

这段代码经历了大概30分钟的修改和debug,然后才逐渐稳定下来,真的很难想象如果是在面试的环境下,在纸笔之上会有如何的表现,可能真是只有天知道了。

分享到:
评论

相关推荐

    数据结构常用算法演示

    - **最短路径算法**:Dijkstra算法、Bellman-Ford算法,用于找出图中两个节点间的最短路径。 - **最小生成树算法**:Prim算法、Kruskal算法,用于找寻连接图中所有节点的权值最小的树。 - **动态规划**:通过分解...

    数据结构资料(试题精选及答案)

    二叉树是最简单的树形结构,每个节点最多有两个子节点。图是由节点和连接这些节点的边组成,可以用来表示复杂的网络关系。 接下来,我们讨论这些数据结构的操作。例如,查找操作在数组中通常可以通过索引直接完成,...

    数据结构和算法的FLASH演示

    二叉排序树(BST)是一种特殊的二叉树,满足左子节点小于父节点,右子节点大于父节点。演示可能会详细解释删除节点时如何保持树的平衡和排序性质。 7. **B树的生长过程.swf**: B树是一种自平衡的多路搜索树,...

    acm常用算法

    - 最长公共子序列(LCS):寻找两个序列中最长的不降序的公共子序列,用于比较文本相似性。 - 状态转移方程:根据问题特性构建状态转移矩阵或方程,以解决复杂问题,如矩阵链乘法。 5. **字符串处理**: - KMP...

    Algorithm:此项目旨再梳理到Android系统或源码中使用到的算法

    2.将铺平的左子树赋予根节点的右子树,再创建右子树的temp 并遍历到末尾 在末尾将铺平的右子树插入(根节点右子树中) 3.返回根节点 4.(Android) 一对多关系 找寻decorView下的特定id的子view 思想: 获取其子view的...

    数据结构(C语言版) 清华大学出版社 第九章查找 例题答案

    查找算法在实际应用中无处不在,例如在数据库中寻找特定记录、在字典中查找单词或在电话簿中找寻联系人。根据描述,该压缩包可能包含了第九章的例题解答,这些解答可以帮助读者更好地理解和掌握查找算法的实现。 ...

    c语言常用算法

    - 最长公共子序列:寻找两个序列中没有顺序关系的最长相同子序列。 - 矩阵链乘法:优化多个矩阵相乘的计算过程,降低计算复杂度。 5. 图论算法: - 广度优先搜索(BFS):用于遍历无权图,常用于寻找最短路径...

    《数据结构》教案(同名17691).docx

    - 树和二叉树:树是一种层次结构,二叉树是特殊的树,每个节点最多有两个子节点。它们在文件系统、编译器设计和图形结构中有广泛应用。例如,Huffman编码利用二叉树实现数据压缩,拓扑排序用于任务调度。 - 图:由...

    中国科技大学2011考研计算机复试真题与答案.pdf

    - **二叉树的构建与遍历**:从文件中读取二叉树节点信息并建立二叉树,可能还需要进行前序、中序或后序遍历。 3. **形式语言与自动机理论**: - **正规式**:题目要求找到一个正规式,使得其表示的语言与给定的...

    java算法大全源码包.rar

    - **链表**:由一系列节点组成,每个节点包含数据和指向下一个节点的引用。 - **栈**:后进先出(LIFO)的数据结构,常用于函数调用、表达式求值等。 - **队列**:先进先出(FIFO)的数据结构,用于模拟各种等待...

    易语言常用算法整理

    - 最长公共子序列:寻找两个序列最长的子序列,它在原序列中都存在,但不一定连续。 - 矩阵链乘法:通过优化运算顺序减少计算矩阵乘法的时间复杂度。 4. **图论算法**: - 深度优先搜索(DFS):沿着图的一条...

    代码面试最常用的10大算法完整版

    - **KMP算法**:在两个字符串中寻找最长公共子序列,避免了多余的回溯。 - **Rabin-Karp滚动哈希**:用于字符串匹配,通过哈希函数快速计算子串的哈希值,减少比较次数。 5. **图论**: - **最小生成树(MST)**:...

    贪婪算法.rar

    - Dijkstra算法:求解单源最短路径问题,每次都选择当前未访问节点中距离源节点最近的一个进行处理。 - Huffman编码:用于数据压缩,通过构建最优的二叉树来实现字符的高效编码。 贪婪算法的优势在于其简单性和效率...

    Java算法大全(近100种算法打包).zip.zip

    - **快速排序**:采用分治策略,以一个元素为基准,将数组分为两部分,然后递归排序,平均时间复杂度为O(n log n)。 - **归并排序**:同样采用分治策略,将数组合并成有序序列,时间复杂度为O(n log n)。 - **...

    jsgorithms:JavaScript中的算法

    - **链表**:每个节点包含数据和指向下一个节点的引用,便于插入和删除,但随机访问较慢。 - **栈**:后进先出(LIFO)的数据结构,主要用于函数调用、表达式求值等场景。 - **队列**:先进先出(FIFO)的数据...

    C课程设计贪吃蛇小游戏内附完整源码及附件.docx

    - **找寻树**和**平衡二叉树**:虽然不直接用于贪吃蛇,但在类似游戏设计中可能用于优化查找和存储。 - **队列**:可以用于实现蛇的移动序列,确保每次只移动头部,然后更新身体位置。 - **串**(字符串):可能...

Global site tag (gtag.js) - Google Analytics