给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
示例 3:
输入:root = [1,2], p = 1, q = 2
输出:1
为了找到二叉树给定两个节点的公共节点,那必然需要对整个二叉树进行遍历
在此处,引用代码随想录中的一段基础讲解:
如何判断在进行递归的时候是对整个二叉树递归还是只对一棵二叉树的左子树 或者右子树进行递归呢?
搜索一条边
if(递归函数(root.left)) return;
if(递归函数(root.right)) return;
搜索整棵树
left = 递归函数(root.left);
right = 递归函数(root.right);
在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,
如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)。
为什么本体要遍历整棵树而不是一条左边或者右边呢?
public static Node LowstAncestor(Node head,Node no1,Node no2){
if(head ==null ||head == no1 || head == no2){ //base case
return head;
}
Node left = LowstAncestor(head.left,no1,no2);
Node right = LowstAncestor(head.right,no1,no2);
//两种情况:
//1.o1 o2互为公共祖先
//2.o1 o2不互为公共祖先,需要向上寻找参能找到答案
//左树的返回值和右树的返回值如果都不为空,那就返回自己的头部
//如果一棵树既没有O1有没有o2 那必然会返回空
if(left != null && right != null){
return head;
}
//如果没有返回自己的头部,那就说明左右两棵树 ,并不都有返回值
return left != null? left:right;
}
public static Node LowstAncestor(Node head,Node no1,Node no2){
//第一步:确认递归终止条件
if(head ==null ||head == no1 || head == no2){ //base case
return head;
}
//写递归函数 包含已经确认的返回值
Node left = LowstAncestor(head.left,no1,no2);
Node right = LowstAncestor(head.right,no1,no2);
//写递归函数条件
//如果一个节点的左节点返回值空 并且右节点返回值为空 直接返回
if(left == null && right === null){
return null;
}else if(left != null && right == null){//在递归的过程中遇到左边不为空的直接向上返回
return left;
}else if(right != null && left == null){//在递归的过程中遇到右边不为空的直接向上返回
return right;
}else{//直到一个节点 右边和左边都不为空,那这个节点就是我们要找的最近公共祖先
return root;
}