剑指offer题解汇总 Java实现
https://blog.csdn.net/guliguliguliguli/article/details/126089434
本题链接
知识分类篇 - 树 - JZ86 在二叉树中找到两个节点的最近的公共祖先
题目
题目主要信息
-
给定一棵二叉树以及这棵树上的两个节点对应的val值o1和o2,请找到o1和o2的最近公共祖先节点
-
二叉树非空,且每个节点值均不同
方案一 深度优先搜索(易于理解)
深度优先搜索一般用于树或者图的遍历,其他有分支的(如二维矩阵)也适用。它的原理是从初始点开始,一直沿着同一个分支遍历,直到该分支结束,然后回溯到上一级继续沿着一个分支走到底,如此往复,直到所有的节点都有被访问到。
思路
既然要找到二叉树中两个节点的最近公共祖先,那我们可以考虑先找到两个节点全部祖先,就得到了从根节点到目标结点的路径,然后依次比较路径得出谁是最近的祖先。
具体做法
-
利用dfs求得根节点到两个目标结点的路径:每次选择二叉树的一棵子树往下找,同时路径数组增加这个遍历的节点值
-
一旦遍历到了叶子节点也没有,则回溯到父节点,寻找其他路径,回溯时要去掉数组中刚刚加入的元素
-
dfs结束以后,遍历两条路径数组,依次比较元素值
-
找到两条路径的第一个不相同的节点即是最近公共祖先
注意
在刚进入方法时,需要判断flag是否为true
-
如果为true,表示已经找到目标节点,不需要继续遍历下去并添加别的节点,直接返回即可
-
如果为false,那么需要继续寻找指定的节点,记录下找到它的路径
在遍历完当前节点的左子树和右子树以后,需要判断flag是否为true
-
如果为true,表示已经在该节点的左子树或者右子树中找到了目标节点,直接返回即可
-
如果为false,表示当前节点,以及其左子树、右子树中没有满足条件的节点,需要返回该节点的父节点,并深入另一条路径,继续寻找
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* }
*/
public class Solution {
/**
* @param root TreeNode类
* @param o1 int整型
* @param o2 int整型
* @return int整型
*/
private boolean flag = false;
public int lowestCommonAncestor(TreeNode root, int o1, int o2) {
// write code here
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
findPath(list1, root, o1);
flag = false;
findPath(list2, root, o2);
int res = 0;
for (int i = 0; i < list1.size() && i < list2.size(); i++) {
int v1 = list1.get(i);
int v2 = list2.get(i);
if (v1 == v2) {
res = v1;
} else {
break;
}
}
return res;
}
private void findPath(ArrayList<Integer> list, TreeNode root, int o) {
if (flag || root == null) {
return;
}
list.add(root.val);
if (root.val == o) {
flag = true;
return;
}
findPath(list, root.left, o);
findPath(list, root.right, o);
if (flag) {
return;
}
list.remove(list.size() - 1);
}
}
方案二 二叉树递归
递归是一个过程或函数在其定义或说明中有直接调用或间接调用自身的一种说法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的、规模较小的问题来求解。因此递归过程,最重要的就是查看能不能将原本的问题分解为更小的子问题,这是使用递归的关键。
而二叉树的递归,则是将某个节点的左子树、右子树看成一棵完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。
思路
-
如果o1和o2中的任意一个和root匹配,那么root就是最近公共祖先
-
如果都不匹配,则分别递归左子树和右子树
-
如果有一个节点出现在左子树、另一个节点出现在右子树,则root就是最近公共祖先
-
如果两个节点都出现在左子树,则说明最近公共祖先出现在左子树中,否则在右子树
-
继续递归左、右子树,直到遇到第1步或第3步的情况
import java.util.*;
public class Solution {
public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
//该子树没找到,返回-1
if(root == null)
return -1;
//该节点是其中某一个节点
if(root.val == o1 || root.val == o2)
return root.val;
//左子树寻找公共祖先
int left = lowestCommonAncestor(root.left, o1, o2);
//右子树寻找公共祖先
int right = lowestCommonAncestor(root.right, o1, o2);
//左子树为没找到,则在右子树中
if(left == -1)
return right;
//右子树没找到,则在左子树中
if(right == -1)
return left;
//否则是当前节点
return root.val;
}
}
注意
当某一结点的left和right值都不为-1的时候,该节点就是题目中要求的两节点的最近的公共祖先。需要多debug几次,了解它的执行流程。