最近看了一道面试题目,觉得很有意思,而且常常被问到,今天综合归纳了一下这道题目,并给出了各种变形题目,附上JAVA版的程序解答。

题目是这样的:寻找二叉树的最低公共祖先?(其中隐含着一个盲点:树是什么树?排序二叉树、普通二叉树、或者不是二叉树?)所以要分别考虑哈各种情况哈

形式一:当树为二叉排序树,如何寻找给定两节点的最低公共祖先?

形式二:当树为普通树,但每个节点中有指针指向其父节点,如何寻找?

形式三:当树为二叉树,每个节点仅有左右孩子指针,如何寻找?

形式四:当树为普通树,每个节点仅有左右孩子指针,如何寻找?


分析:此题设计考点很多,单细细分析下来,都可以逐渐解决的(为了更加通用化,在对树的定义上加了泛型):

(1)排序二叉树

排序二叉树相对简单,根据排序二叉树的特点,根节点的左孩子永远小于根节点的值,右孩子的值永远大于根节点的值,对给定节点Node1、Node2,要找其最低公共祖先=>实际上就是寻找一个节点,使得节点的值位于两个节点的值中间,即原问题转化为二叉树的遍历问题。

     5   ======排序二叉树/ \3   7
     / \       / \2  4 6  9
/
1   

树的节点定义如下:

class TreeNode<T> {T data;TreeNode<T> leftChildren;TreeNode<T> rightChildren;public TreeNode(T data){this.data=data;this.leftChildren=null;   //此处是我的强迫症this.rightChildren=null;}
}

实现寻找父节点的核心代码如下:

 //通过遍历二叉排序树即可完成查找private static TreeNode<Character> findParent(TreeNode<Character> root,TreeNode<Character> node_1, TreeNode<Character> node_2) {if(root==null)return null;if(root.data>node_1.data && root.data>node_2.data)return findParent(root.leftChildren,node_1,node_2);else if(root.data<node_1.data && root.data<node_2.data)return findParent(root.rightChildren,node_1,node_2);elsereturn root;   //用此条件判断两数之间也可(root.data-node_1.data)*(root.data-node_2.data)<0  }

实例:如上图排序二叉树,寻找节点2和节点4的公共最低祖先,结果为:

inOrder: 1 2 3 4 5 6 7 9
parent:3

(2)普通树(每个节点有一个指向父节点的指针)

当树为普通树,但含有父节点指针的时候,要寻找公共节点,在节点正确给定的情况下,两者最高层的祖先就是根节点,实际上,从Node1,通过父节点指针,可以找到到Node1的所有祖先(最后一个为根节点),同理,也可以找到Node2节点的所有祖先,因此,本题即转化为=>求两链表的第一个公共节点(链表不一定等长)。

      A    ======普通树(带父节点指针)/|\B C K/  \D    E
/ \   /|\
F   G H I J

此题最重要的就是思想的转换。

树的节点定义为:

class TreeNode<T>{T data;TreeNode<T> parent=null;//若带父节点则加上//将其孩子节点存储为一个集合ArrayList<TreeNode<T>> children=new ArrayList<TreeNode<T>>();TreeNode(T data){this.data=data;}
}

寻找最低公共祖先核心代码:

 //寻找两链表的第一个公共节点private static TreeNode<Character> findParent(TreeNode<Character> root,TreeNode<Character> node1, TreeNode<Character> node2) {//预防空输入if(node1==null || node2==null)return null;//得到两链表的长度int len1=1,len2=1;TreeNode<Character> temp=node1;while(temp.parent!=null){len1++;temp=temp.parent;}temp=node2;while(temp.parent!=null){len2++;temp=temp.parent;}//长度小的链表先移动int step=Math.abs(len1-len2);TreeNode<Character> tempLong=len1>=len2?node1:node2;TreeNode<Character> tempShort=len1<len2?node1:node2;for(int i=0;i<step;i++){tempLong=tempLong.parent;}//两链表一起移动,直到找到相等的节点则返回for(int i=0;i<Math.min(len1, len2);i++){if(tempLong.data.equals(tempShort.data)){return tempLong;}tempLong=tempLong.parent;tempShort=tempShort.parent;}return null;}
 //普通树的前序遍历public static void preOrder(TreeNode<Character> root){if(root!=null){System.out.print(root.data+" ");for(int i=0;i<root.children.size();i++){preOrder(root.children.get(i));}}}

实例:如上图所示,寻找节点D和节点J的最低公共祖先,结果如下:

preOrder: A B D F G E H I J C K (此处打印的是树的前序遍历)
parent:B

(3)普通二叉树(无指向父节点的指针)

当无指向父节点的指针时,要想找到最低公共祖先,可结合第二题的思维方式,考虑从根节点root开始,找出root到Node1和Node2的路径,通过路径找最低公共祖先,于是原问题转化为=>寻找二叉树中根节点到指定节点的路径(需要利用回溯的思想)+寻找链表最后一个公共节点的问题。

      A    ======普通二叉树/\B  C/  \D    E
/ \   /\
F   G H  I

树节点的定义:

class TreeNode<T> {T data;TreeNode<T> leftChildren;TreeNode<T> rightChildren;public TreeNode(T data){this.data=data;this.leftChildren=null;   //此处为强迫症this.rightChildren=null;}
}

寻找的过程主要分为两步:先找出路径,再求路径的最后一个公共节点,代码如下:

 //寻找两个链表的最后一个公共节点private static TreeNode<Character> findParent(ArrayList<TreeNode<Character>> path1,ArrayList<TreeNode<Character>> path2) {if(path1.size()==0 || path2.size()==0)return null; TreeNode<Character> parent=null;int len=path1.size()<path2.size()?path1.size():path2.size();for(int i=0;i<len;i++){if(path1.get(i).data.equals(path2.get(i).data)){parent=path1.get(i);}}return parent;}
    
 //回溯方法寻找根节点到某节点的路径private static boolean findPath(TreeNode<Character> root,TreeNode<Character> node,ArrayList<TreeNode<Character>> path) {if(root==null || node==null){return false;}path.add(root);if(root.data.equals(node.data)){return true;}//递归左右孩子节点if(findPath(root.leftChildren,node,path)){return true;}if(findPath(root.rightChildren,node,path)){return true;}//为了回溯的正确性,必须由此操作path.remove(path.size()-1);return false;}

实例:寻找节点H到节点C的最低公共祖先,运行结果如下:

preOrder: A B D F G E H I C path1: A B E H path2: A C parent:A

(4)普通树

对二叉树的操作,总是仅涉及最多左右孩子两个指针的问题,然而对于具有普通结构的树,每个节点的孩子节点数不同,我们就需要改变树节点的定义,可以考虑用一个结合来存储树的孩子节点的信息,思路同(3)一样,仅仅是在寻找路径时有点变化,即:(3)可看作(4)的特殊情况。

此次问题的升级,主要通过改变树的定义来实现的

      A    ======普通树(不带父节点指针)/|\B C K/  \D    E
/ \   /|\
F   G H I J

树的定义:

class TreeNode<T>{T data;//将其孩子节点存储为一个集合ArrayList<TreeNode<T>> children=new ArrayList<TreeNode<T>>();TreeNode(T data){this.data=data;}
}

充分利用JAVA集合的优点,将树的节点存储在集合中。

核心代码:

 //寻找两个链表的最后一个公共节点private static TreeNode<Character> findParent(ArrayList<TreeNode<Character>> path1,ArrayList<TreeNode<Character>> path2) {if(path1.size()==0 || path2.size()==0)return null; TreeNode<Character> parent=null;int len=path1.size()<path2.size()?path1.size():path2.size();for(int i=0;i<len;i++){if(path1.get(i).data.equals(path2.get(i).data)){parent=path1.get(i);}}return parent;}//回溯方法寻找根节点到某节点的路径private static boolean findPath(TreeNode<Character> root,TreeNode<Character> node,ArrayList<TreeNode<Character>> path) {if(root==null || node==null){return false;}path.add(root);if(root.data.equals(node.data)){return true;}//与二叉树相比,由递归遍历左右孩子变为了递归遍历所有孩子节点for(int i=0;i<root.children.size();i++){if(findPath(root.children.get(i),node,path)){return true;}}//为了回溯的正确性,必须有此操作path.remove(path.size()-1);return false;}

实例:寻找节点G与节点J的最低公共祖先,运行结果如下:

preOrder: A B D F G E H I J C K path1: A B E J path2: A B D G parent:B      //正确的找到了最低公共祖先

总结:可以看到,从一个排序二叉树问题逐渐深入,最本质的,还是我们掌握的基本算法,尤其是递归方法,树的操作中大量的用到了递归;对于排序二叉树,针对特点,比较容易想到办法;对带有父指针的普通树,可以将问题转化为求链表的第一个公共节点的问题;没有父指针,就需要用找到路径,求链表最后一个公共节点;这些问题都是一些简单问题的综合,在思考问题时,我们需要灵活一定,想方设法的去转化、分解问题,而不是马上去查答案。

由浅入深:求给定两个树节点的最低公共祖先(二叉树、普通树结构)JAVA实现相关推荐

  1. 2022-05-22:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p

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

  2. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先

    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 三种情况: 1).如果左边为空,右边不为空,则右边的第一个节点就为公共祖先 2).如果右边为空,左边不为空,则左边的第一个节点就为公共祖先 3) ...

  3. Java 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先

    代码中的二叉树长这个样子↓↓↓ 找到该树中两个指定节点的最近公共祖先,有三种情况,如图: import java.util.*; import java.util.Queue; //下面的所有求结点总 ...

  4. 树如何找共同祖先_如何找到任何二叉树中两个节点的最低公共祖先?

    小编典典 尼克·约翰逊是正确的,一个一个O(n)的时间复杂度算法是最好的,如果你没有父指针,你可以做.)对于算法的一个简单的递归版本中看到代码金丁的职务)它运行在O(n)的时间. 但是请记住,如果您的 ...

  5. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

    百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x,满足 x 是 p.q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先).&q ...

  6. 二叉树如何求两个叶节点的最近公共祖先--re

    二叉搜索树原理: 二叉搜索树是排序过的 ,位于左子树的结点都比父结点小,位于右子树的结点都比父结点大,我们只需从根节点开始和两个输入的结点进行比较,如果当前节点的值比两个结点的值都大,那么最低的公共祖 ...

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

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

  8. Leetcode236 最近公共祖先-二叉树两次遍历

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

  9. 二叉树——求两个节点的最近公共祖先

    题目 给定一颗二叉树的头结点,和这颗二叉树中2个节点n1和n2,求这两个节点的最近公共祖先: 思路 利用后序遍历实现: 对于当前节点cur,如果节点为null或者等于n1或n2中的一个,则直接返回cu ...

最新文章

  1. matlab求传递函数在某个频率点的增益_【干货分享】轻松弄懂开关电源TL431环路补偿传递函数推导...
  2. 单链表的创建示意图, 显示单向链表的分析
  3. 《HTML5 canvas开发详解(第2版)》——1.3 本书使用的基础HTML页面
  4. ThinkPHP3.2.3 字段映射/自动验证/自动完成
  5. Keil forc51安装教程
  6. 【洛谷 1969】积木大赛
  7. Rime在linux下面的安装-还没写完
  8. webpack进阶之插件篇
  9. Android应用安全开发之浅谈网页打开APP
  10. C语言——输出*菱形
  11. 阿里云如何二次驱动云计算
  12. 函数式编程能否支持更高效的区块链基础设施?
  13. ThinkPHP框架短信接口
  14. 数据库表关系详解(一对多、一对一、多对多)
  15. word添加参考文献和标注的方法
  16. python3.8零基础入门教程_正版 Python 3.8编程快速入门 针对wan全零基础入门的读者 采用*小化安装+极简代码的教学...
  17. 【数据集】Kinetics-600 dataset介绍
  18. NVIDIA NeMo学习笔记
  19. 经典VU仪表的混音效果工具
  20. 英语练习91 Society is changing rapidly.

热门文章

  1. Tensorflow—TFRecord文件生成与读取
  2. 开源自主导航小车MickX4(十)总结
  3. 计算机实战项目之 [含论文+开题报告+答辩PPT+源码等]基于ssm+bootsrap框架的人力资源考勤系统
  4. IINA 1.1.0beta1中文版 - Mac最强万能视频播放器
  5. python语音朗读
  6. JavaScript day01笔记
  7. Android 短信模块分析(五) MMS之彩信的发送与接收
  8. python笔记(骚操作)
  9. 微信小程序(模仿头条新闻)
  10. 2018EI收录情况