程序员面试100题之十六:二叉树中两个节点的最近公共父节点(最低的二叉树共同祖先)

这个问题可以分为三种情况来考虑:
情况一:root未知,但是每个节点都有parent指针

此时可以分别从两个节点开始,沿着parent指针走向根节点,得到两个链表,然后求两个链表的第一个公共节点,这个方法很简单,不需要详细解释的。

情况二:节点只有左、右指针,没有parent指针,root已知
思路:有两种情况,一是要找的这两个节点(a, b),在要遍历的节点(root)的两侧,那么这个节点就是这两个节点的最近公共父节点;

二是两个节点在同一侧,则 root->left 或者 root->right 为 NULL,另一边返回a或者b。那么另一边返回的就是他们的最小公共父节点。

递归有两个出口,一是没有找到a或者b,则返回NULL;二是只要碰到a或者b,就立刻返回。

  1. // 二叉树结点的描述
  2. typedef struct BiTNode
  3. {
  4. char data;
  5. struct BiTNode lchild, rchild; // 左右孩子
  6. }BinaryTreeNode;
  7. // 节点只有左指针、右指针,没有parent指针,root已知
  8. BinaryTreeNode findLowestCommonAncestor(BinaryTreeNode root , BinaryTreeNode* a , BinaryTreeNode* b)
  9. {
  10. if(root == NULL)
  11. return NULL;
  12. if(root == a || root == b)
  13. return root;
  14. BinaryTreeNode* left = findLowestCommonAncestor(root->lchild , a , b);
  15. BinaryTreeNode* right = findLowestCommonAncestor(root->rchild , a , b);
  16. if(left && right)
  17. return root;
  18. return left ? left : right;
  19. }

情况三: 二叉树是个二叉查找树,且root和两个节点的值(a, b)已知
  1. // 二叉树是个二叉查找树,且root和两个节点的值(a, b)已知
  2. BinaryTreeNode* findLowestCommonAncestor(BinaryTreeNode* root , BinaryTreeNode* a , BinaryTreeNode* b)
  3. {
  4. char min , max;
  5. if(a->data < b->data)
  6. min = a->data , max = b->data;
  7. else
  8. min = b->data , max = a->data;
  9. while(root)
  10. {
  11. if(root->data >= min && root->data <= max)
  12. return root;
  13. else if(root->data < min && root->data < max)
  14. root = root->rchild;
  15. else
  16. root = root->lchild;
  17. }
  18. return NULL;
  19. }






1、二叉树定义:

  1. typedef struct BTreeNodeElement_t_ {
  2. void *data;
  3. } BTreeNodeElement_t;
  4. typedef struct BTreeNode_t_ {
  5. BTreeNodeElement_t *m_pElemt;
  6. struct BTreeNode_t_ *m_pLeft;
  7. struct BTreeNode_t_ *m_pRight;
  8. } BTreeNode_t;


2、查找二叉树中两个节点的最低祖先节点(或最近公共父节点等)

    最低祖先节点就是从根节点遍历到给定节点时的最后一个相同节点

例如:

                                                                                    A

                                                            B                                            C

                                            D                        E                     F                                G

                                    H            I            J            K        L        M                        N    O

如上图,H和J的最低祖先节点是B。

因为从根节点A到H的链路为:   A     B    D   H

从根节点A到J的链路为:   A   B    E   J

查看链路节点可知,B是最后一个相同节点,也就是所谓的最近公共父节点或者说最低祖先节点。


(1)递归方式

如果给定pRoot是NULL,即空树,则返回的公共节点自然就是NULL;

如果给定pRoot与两个节点中任何一个相同,说明,pRoot在就是所要找的两个节点之一,则直接返回pRoot,表明在当前链路中找到至少一个节点;

如果给定pRoot不是两个节点中任何一个,则说明,需要在pRoot的左右子树中重新查找,此时有三种情况:两个节点都在左子树上;两个节点都在右子树上;一个在左子树,一个在右子树上;具体来说,就是:

        情况一:如果左子树查找出的公共节点是NULL,则表明从左子树根节点开始到左子树的所有叶子节点等所有节点中,没有找到两个节点中的任何一个,这就说明,这两个节点不在左子树上,不在左子树,则必定在右子树上;

       情况二:如果右子树查找的公共节点是NULL,说明在右子树中无法找到任何一个节点,则两个节点必定在左子树上;

       情况三: 如果左右子树查找的公共节点都不是NULL,说明左右子树中各包含一个节点,则当前节点pRoot就是最低公共节点,返回就可以了。

       三种情况是互斥的, 只能是其中之一。

  1. BTreeNode_t *GetLastCommonParent( BTreeNode_t *pRoot, BTreeNode_t *pNode1, BTreeNode_t *pNode2){
  2. if( pRoot == NULL ) //说明是空树,不用查找了,也就找不到对应节点,则返回NULL
  3. return NULL;
  4. if( pRoot == pNode1 || pRoot == pNode2 )//说明在当前子树的根节点上找到两个节点之一
  5. return pRoot;
  6. BTreeNode_t *pLeft = GetLastCommonParent( pRoot->m_pLeft, pNode1, pNode2); //左子树中的查找两个节点并返回查找结果
  7. BTreeNode_t *pRight = GetLastCommonParent( pRoot->m_pRight, pNode1, pNode2);//右子树中查找两个节点并返回查找结果
  8. if( pLeft == NULL )//如果在左子树中没有找到,则断定两个节点都在右子树中,可以返回右子树中查询结果;否则,需要结合左右子树查询结果共同断定
  9. return pRight;
  10. if ( pRight == NULL )//如果在右子树中没有找到,则断定两个节点都在左子树中,可以返回左子树中查询结果;否则,需要结合左右子树查询结果共同断定
  11. return pLeft;
  12. return pRoot;//如果在左右子树中都找两个节点之一,则pRoot就是最低公共祖先节点,返回即可。
  13. }


(2)非递归方式:

  1. BTreeNode_t *GetLastCommonParent(BTreeNode_t *pRoot, BTreeNode_t *pNode1, BTreeNode_t *pNode2){
  2. if( pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
  3. return NULL;
  4. vector < BTreeNode_t *> vec1;//用来保存从根节点到指定节点的遍历路径,前序遍历
  5. vector <BTreeNode_t *> vec2;
  6. stack <BTreeNode_t *> st;
  7. bool findflag1 = false;
  8. bool findflag2 = false;
  9. BTreeNode_t *commonParent = NULL;
  10. while( pRoot != NULL || !st.empty() ){
  11. while( pRoot != NULL ){
  12. if( findflag1 == false){//没有找出所有的节点:从根节点到指定节点,在遍历时继续入栈
  13. vec1.push_back( pRoot);
  14. if( pRoot == pNode1)//找到,则设置标志位
  15. findflag1 = true;
  16. }
  17. if( findflag2 == false ){
  18. vec1.push_back( pRoot);
  19. if( pRoot == pNode2 )
  20. findflag2 = true;
  21. }
  22. if( findflag1 == true && findflag2 == true)//如果都已找到,则退出
  23. break;
  24. st.push( pRoot);
  25. pRoot = pRoot->m_pLeft;
  26. }
  27. while( !st.empty()){
  28. pRoot = st.top();
  29. st.pop();
  30. pRoot = pRoot->right;
  31. if( findflag1 == false )//没有找到全部路径节点时,就需要将错误路径节点退出
  32. vec1.pop_back();
  33. if( findflag2 == false )
  34. vec2.pop_back();
  35. }
  36. if( findflag1 == true && findflag2 == true)//如果都已找到,则退出
  37. break;
  38. }
  39. if( findflag1 == true && findflag2 == true){//在两个遍历路径上查找最后一个相同的节点,就是最低公共祖先节点(最近公共父节点)
  40. vector< BTreeNode_t *> ::iterator iter1 = vec1.begin();
  41. vector< BTreeNode_t *> ::iterator iter2 = vec2.begin();
  42. while( iter1 != vec1.end() && iter2 != vec2.end() ){
  43. if( *iter1 == *iter2)
  44. commonParent = *iter1;
  45. else
  46. break;
  47. ++iter1;
  48. ++iter2;
  49. }
  50. }
  51. vec1.clear();
  52. vec2.clear();
  53. st.clear();
  54. return commonParent;
  55. }


</pre><pre>

 

 


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/481079.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

肖仰华 | 基于知识图谱的问答系统

本文转载自公众号知识工场。 本文整理自复旦大学知识工场肖仰华教授在VLDB 2017 会议上的论文报告&#xff0c;题目为《KBQA: Learning Question Answering over QA Corpora and Knowledge Bases》&#xff0c;作者包括&#xff1a;崔万云博士&#xff08;现上海财经大学讲师&a…

【LeetCode】3月21日打卡-Day6

题1 水壶问题 描述 有两个容量分别为 x升 和 y升 的水壶以及无限多的水。请判断能否通过使用这两个水壶&#xff0c;从而可以得到恰好 z升 的水&#xff1f; 如果可以&#xff0c;最后请用以上水壶中的一或两个来盛放取得的 z升 水。 你允许&#xff1a; 装满任意一个水壶 清空…

【小夕精选】YJango 7分钟带你领略你未曾想过的线性代数+微积分

小夕很早之前就想转一些精彩的技术文章&#xff0c;这样哪怕没有时间写作的时候&#xff0c;也能把优质的干货分享给大家&#xff5e;然鹅&#xff0c;由于我也不知道是什么的原因&#xff0c;就不小心拖到了现在╮(&#xffe3;▽&#xffe3;"")╭之前有不少粉丝希…

【LeetCode】3月22日打卡-Day7

题1 描述 给定整数数组 A&#xff0c;每次 move 操作将会选择任意 A[i]&#xff0c;并将其递增 1。 返回使 A 中的每个值都是唯一的最少操作次数。 示例 1: 输入&#xff1a;[1,2,2] 输出&#xff1a;1 解释&#xff1a;经过一次 move 操作&#xff0c;数组将变为 [1, 2, 3]。…

手写实现李航《统计学习方法》书中全部算法

https://github.com/Dod-o/Statistical-Learning-Method_Code

白硕 | 基于区块链的众包社区激励机制

本文整理自白硕老师在 YOCSEF 武汉专题论坛&#xff1a;“人工智能遇到区块链&#xff0c;是惊鸿一瞥还是天长地久&#xff1f;”的报告。 很高兴有这个机会跟大家交流。我先讲几个案例作为引子。第一个案例与知识图谱有关。这个公司做的是非常垂直的一个领域&#xff0c;安全教…

【小夕精选】多轮对话之对话管理(Dialog Management)

这一篇是一段时间之前小夕初入对话领域时刷到的徐阿衡小姐姐写的一篇文章&#xff0c;写的深入浅出&#xff0c;十分适合有一定基础的情况下想快速了解对话管理技术的童鞋阅读~另外顺手推一下阿衡小姐姐的订阅号「徐阿衡」&#xff0c;干货满满不要错过哦~这一篇想写一写对话管…

【LeetCode】3月23日打卡-Day8

题1 链表的中间结点 描述 给定一个带有头结点 head 的非空单链表&#xff0c;返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;[1,2,3,4,5] 输出&#xff1a;此列表中的结点 3 (序列化形式&#xff1a;[3,4,5…

KD Tree的原理及Python实现

1. 原理篇我们用大白话讲讲KD-Tree是怎么一回事。1.1 线性查找假设数组A为[0, 6, 3, 8, 7, 4, 11]&#xff0c;有一个元素x&#xff0c;我们要找到数组A中距离x最近的元素&#xff0c;应该如何实现呢&#xff1f;比较直接的想法是用数组A中的每一个元素与x作差&#xff0c;差的…

漆桂林 | 知识图谱的应用

本文作者为东南大学漆桂林老师&#xff0c;首发于知乎专栏知识图谱和智能问答 前面一篇文章“知识图谱之语义网络篇”已经提到了知识图谱的发展历史&#xff0c;回顾一下有以下几点&#xff1a; 1. 知识图谱是一种语义网络&#xff0c;即一个具有图结构的知识库&#xff0c;这里…

NLP预训练之路——从word2vec, ELMo到BERT

前言 还记得不久之前的机器阅读理解领域&#xff0c;微软和阿里在SQuAD上分别以R-Net和SLQA超过人类&#xff0c;百度在MS MARCO上凭借V-Net霸榜并在BLEU上超过人类。这些网络可以说一个比一个复杂&#xff0c;似乎“如何设计出一个更work的task-specific的网络"变成了NLP…

论文 | 信息检索结果Ranking的评价指标《RankDCG: Rank-Ordering Evaluation Measure》

未经允许&#xff0c;不得转载&#xff0c;谢谢~~ 一 文章简介 为什么要提出这个新的评价算法&#xff1f; 我们都知道ranking过程对于信息检索的结果是非常重要的&#xff0c;那么我们就需要有一些算法能评价ranking的结果到底如何。现有用来评价ranking的常用算法有&#xff…

【LeetCode】3月24日打卡-Day9

题1 按摩师 描述 一个有名的按摩师会收到源源不断的预约请求&#xff0c;每个预约都可以选择接或不接。在每次预约服务之间要有休息时间&#xff0c;因此她不能接受相邻的预约。给定一个预约请求序列&#xff0c;替按摩师找到最优的预约集合&#xff08;总预约时间最长&#x…

肖仰华 | 基于知识图谱的用户理解

本文转载自公众号知识工场。 本文整理自肖仰华教授在三星电子中国研究院做的报告&#xff0c;题目为《Understanding users with knowldge graphs》。 今天&#xff0c;很高兴有这个机会来这里与大家交流。 前面两位老师把基于社会影响力的传播和推荐&#xff0c;以及跨领域的…

【LeetCode】3月25日打卡-Day10

题1 三维形体的表面积 描述 在 N * N 的网格上&#xff0c;我们放置一些 1 * 1 * 1 的立方体。 每个值 v grid[i][j] 表示 v 个正方体叠放在对应单元格 (i, j) 上。 请你返回最终形体的表面积。 示例 1&#xff1a; 输入&#xff1a;[[2]] 输出&#xff1a;10 示例 2&#xf…

NLP的游戏规则从此改写?从word2vec, ELMo到BERT

前言还记得不久之前的机器阅读理解领域&#xff0c;微软和阿里在SQuAD上分别以R-Net和SLQA超过人类&#xff0c;百度在MS MARCO上凭借V-Net霸榜并在BLEU上超过人类。这些网络可以说一个比一个复杂&#xff0c;似乎“如何设计出一个更work的task-specific的网络"变成了NLP领…

信息检索IR评价中常见的评价指标-MAP\NDCG\ERR\P@10等

信息检索评价是对信息检索系统性能&#xff08;主要满足用户信息需求的能力&#xff09;进行评估的活动。通过评估可以评价不同技术的优劣&#xff0c;不同因素对系统的影响&#xff0c;从而促进本领域研究水平的不断提高。信息检索系统的目标是较少消耗情况下尽快、全面返回准…

科普 | “开放知识”的定义

OpenKG 主要关注知识图谱数据&#xff08;或者称为结构化数据、语义数据、知识库&#xff09;的开放&#xff0c;广义上 OpenKG 属于开放数据的一种。 关于“开放”的定义&#xff0c;比较好的参考是由国际开放知识基金会 OKFN.ORG(Open Knowledge International)给与的“开放知…

【LeetCode】3月26日打卡-Day11

题1 车的可用捕获量 描述 在一个 8 x 8 的棋盘上&#xff0c;有一个白色车&#xff08;rook&#xff09;。也可能有空方块&#xff0c;白色的象&#xff08;bishop&#xff09;和黑色的卒&#xff08;pawn&#xff09;。它们分别以字符 “R”&#xff0c;“.”&#xff0c;“B…