转自:http://blog.csdn.net/hhygcy/article/details/4660362

很流行的一个问题,常见于各种面试中,http://fayaa.com/tiku/view/16/ 这里有一个很好的汇总.

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

情况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)步之后,齐头并进,就能解决了。

[cpp] view plaincopy
  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. // skip the abs(length1-length2)
  17. bstNode* pIter1 = NULL;
  18. bstNode* pIter2 = NULL;
  19. int k=0;
  20. if (length1>=length2)
  21. {
  22. bstNode* pTemp = pNode1;
  23. while (k++<length1-length2)
  24. {
  25. pTemp = pTemp->pParent;
  26. }
  27. pIter1 = pTemp;
  28. pIter2 = pNode2;
  29. }
  30. else
  31. {
  32. bstNode* pTemp = pNode1;
  33. while (k++<length2-length1)
  34. {
  35. pTemp = pTemp->pParent;
  36. }
  37. pIter1 = pNode1;
  38. pIter2 = pTemp;
  39. }
  40. while (pIter1&&pIter2&&pIter1!= pIter2)
  41. {
  42. pIter1 = pIter1->pParent;
  43. pIter2 = pIter2->pParent;
  44. }
  45. return pIter1;
  46. }

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

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

[cpp] view plaincopy
  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,我们找到了我们的答案。下面的代码完全按照这个思路写成

[cpp] view plaincopy
  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,然后才逐渐稳定下来,真的很难想象如果是在面试的环境下,在纸笔之上会有如何的表现,可能真是只有天知道了。

二叉树两个节点的公共节点相关推荐

  1. 剑指offer之求两个链表的第一个公共节点

    1 问题 输入两个链表,找出它们的第一个公共结点. 含有公共节点的两个链表的结构类似于下图中的链表: 1 -> 2 -> 3 -> 4 ->5 2 -> 4 ->5 ...

  2. java中获取链表的第一个节点,两个链表中的第一个公共节点(java)

    题目描述: 输入两个链表,找出它们的第一个公共结点. 分析: 思路一:暴力解法,强烈不建议! 遍历第一个链表的每个节点,同时每次都遍历一遍另一个链表,看是否有节点和这个节点相同,如果有相同节点就是公共 ...

  3. 剑指Offer+第37题+两个链表的第一个公共节点+java

    题目:输入两个链表,找出它们的第一个公共结点. 面试的时候碰到这道题,很多应聘者的第一反应就是蛮力法:在第一链表上顺序遍历每个结点,没遍历到一个结点的时候,在第二个链表上顺序遍历每个结点.如果在第二个 ...

  4. 【IT笔试面试题整理】寻找二叉树两节点的最近的公共祖先

    [试题描述] 求二叉树中任意两个节点的最近公共祖先也称为LCA问题(Lowest Common Ancestor). 二叉查找树 如果该二叉树是二叉查找树,那么求解LCA十分简单. 基本思想为:从树根 ...

  5. 数据结构-寻找二叉树两节点的最近公共祖先(Java)

    寻找最近公共祖先 题目 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个节点 p.q,最近公共祖先表示为一个节点 x,满足 ...

  6. 二叉树两个节点最近公共祖先的解法

    假设有一个二叉树.根节点为TreeNode root.p节点和 q节点均在二叉树中.求p和q的最近公共祖先节点. 优雅的递归解法: 对于这个问题, 我们定义一个函数.但是这个函数扩充这个问题的定义 p ...

  7. 寻找二叉树两个结点的最低共同父节点

    寻找二叉树两个结点的最低共同父节点 题目:二叉树的结点的定义如下: struct TreeNode {int m_nValue;TreeNode *m_pLeft;TreeNode *m_pRight ...

  8. 【Java】剑指 Offer 52. 两个链表的第一个公共节点

    题目 :输入两个链表,找出它们的第一个公共节点. 算法思路 : 首先我们要明确,两个链表相交,是Y形状的 两个链表相交,是next域相同 因为两个单链表的长度是不一样的,所以我们需要让长的那个链表,引 ...

  9. 算法------ 两个链表的第一个公共节点

    题目: 输入两个链表,找出它们的第一个公共节点. 如下面的两个链表: 在节点 c1 开始相交. 注意: 如果两个链表没有交点,返回 null. 在返回结果后,两个链表仍须保持原有的结构. 可假定整个链 ...

最新文章

  1. redis延迟队列 实现_灵感来袭,基于Redis的分布式延迟队列(续)
  2. pytorch 笔记:tensorboardX
  3. Java数三退一问题代码_数三退一问题算法(Java)
  4. 【Mail】telnet收发邮件过程
  5. 约瑟夫环(丢手绢问题)
  6. 蔡崇信与马云的 20 年
  7. Log4net日志记录包
  8. 前端特效-霓虹灯按钮
  9. 分类信息采集发布采集器软件
  10. Windows 10 Enterprise LTSC 2019 (x64) 版本 (安装+激活+添加系统邮箱)
  11. 微型计算机的性能主要由微处理器的什么,微型计算机的性能主要由微处理器的什么决定...
  12. 关于D3D中AGP显存,内存,显存三种内存的解释
  13. assimp批量转模型_IGS模型批量转换成STL模型
  14. 中国计算机科学院士,盘点!获奖者中,84位院士、10位国家最高科学技术奖得主,高校科学家表现出色...
  15. Win10安装Kali子系统
  16. 机器学习 逻辑回归算法应用案例
  17. 吉特仓库管理系统-ORM框架的使用
  18. SQL 中的LTRIM()和RTTIM()的用法
  19. 纤亿通带你去看空分复用光纤技术突破
  20. Win炫酷实用快捷键及触控板手势

热门文章

  1. 我的Java教程,不断整理,反复学习,记录着那些年大学奋斗的青春
  2. 直播预告 | AAAI 2022论文解读:融入知识图谱的分子对比学习
  3. 深圳招聘 | 元象唯思:决策AI研发工程师、NLP算法工程师(可实习)
  4. FewRel 2.0数据集:以近知远,以一知万,少次学习新挑战
  5. 如何独立实现一个基于知识图谱的问答系统
  6. 和49支战队瓜分600万奖金,这场史上奖金最多的AI大赛到底比什么?
  7. 二维计算几何基础知识
  8. ffmpeg 声音参数_ffmpeg转换参数和压缩输出大小的比率
  9. excel 两组数据交点_Python 自动化测试(四):数据驱动
  10. oracle protocol=beq 不可用,学习笔记:Oracle数据库坏块 深入研究obj$坏块导致exp/expdp不能执行原因...