题目描述: 题目要求简单易懂,就是在一棵二叉树中找到给定的o1和o2这两个节点的最近的公共祖先。
我们想想,两个节点的最近的公共祖先,无外乎就以下几种情况。
第一种,o1和o2恰好分布在根节点root的两侧,那么最近的公共祖先即为根节点root。
第二种,o1和o2分布在根节点的同一侧,那么则会出现两种情况。
① 首先,出现如下图这种情况,则可以转变为第一种情况,o1和o2分布在了值为5的这个节点的两侧。那么此时o1和o2的最近公共祖先则为值为5这个节点。
② o1和o2恰好的父子节点的关系,那么此时它们最近的公共祖先则为那个父节点的节点。
所以,基于上面情况的分析,我们可以使用递归的方式来解决这道题目。
方法一:递归
直接从根节点往下寻找,如果当前节点既不是o1也不是o2。
- 从左侧寻找o1和o2,当找到其中一个则返回
- 从右侧寻找o1和o2,当找到其中一个则返回
如果当前节点为o1或者o2,则证明当前节点为两者的最近公共祖先。例如,当前节点是o1,那么此时o2必定是o1的子孙节点。所以o1即为两者的最近公共祖先。
代码:
public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
// write code here
return LRD(root,o1,o2).val;
}
public TreeNode LRD(TreeNode root, int o1, int o2){
// 找到o1或者o2,或者遍历到尽头还没找到则返回root(null)
if(root == null || root.val == o1 || root.val == o2)
return root;
// 朝着root节点的左边去寻找o1,o2
TreeNode left = LRD(root.left,o1,o2);
// 朝着root节点的右边去寻找o1,o2
TreeNode right = LRD(root.right,o1,o2);
// 如果root节点的左边没有o1或者o2,则证明o1和o2分布在二叉树的右侧
// 那么此时则直接返回right, right为先找到的节点 o1或者o2
if(left == null)
return right;
// 如果root节点的右边没有o1或者o2,则证明o1和o2分布在二叉树的左侧
// 那么此时则直接返回left, left为先找到的节点 o1或者o2
if(right == null)
return left;
// 否则,当前节点即为o1和o2的最近公共祖先,此时o1和o2分布在root两侧
return root;
}
复杂度分析:
时间复杂度:。n为二叉树的结点数,需要遍历n次。
空间复杂度:。当二叉树退化成链表的时候,此时递归深度为n。
方法二:使用哈希表存储父节点
我们不妨换个思路,因为题目主要就是要找出两个节点的最近公共祖先,出现的所有情况如题目开头分析。所以我们可以使用一个哈希表,先存储所有的节点的父节点。然后利用题目给出的两个节点o1,o2,先取o1,遍历找出其的所有父节点(即父节点的节点都找出来),并记录起来。接着我们取o2,遍历找出其父节点,如果在遍历的过程中,发现其父节点已经访问过了,那么此时这个访问过的节点必定就是两者的最近的公共祖先了。
代码:
Map<Integer, TreeNode> parent = new HashMap<Integer, TreeNode>();
Set<Integer> visited = new HashSet<Integer>();
public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
// 先调用dfs方法
dfs(root);
// 创建两个节点,值为o1,o2
TreeNode node1 = new TreeNode(o1);
TreeNode node2 = new TreeNode(o2);
// 对值为o1的这个节点,向上遍历找出其所有父节点(祖先节点)
while (node1 != null) {
visited.add(node1.val);
// 向上
node1 = parent.get(node1.val);
}
// 向上遍历值为o2的节点
while (node2 != null) {
// 如果已经访问过了,证明这个节点就是两个节点的最近公共祖先
if (visited.contains(node2.val)) {
return node2.val;
}
// 向上
node2 = parent.get(node2.val);
}
return root.val;
}
// dfs先将整棵树中的节点与其父节点的信息记录起来
public void dfs(TreeNode root) {
// 遍历最子树
if (root.left != null) {
parent.put(root.left.val, root);
dfs(root.left);
}
// 遍历右子树
if (root.right != null) {
parent.put(root.right.val, root);
dfs(root.right);
}
}
复杂度分析:
时间复杂度:。n为二叉树的结点数,需要遍历n次。
空间复杂度:。当二叉树退化成链表的时候,此时递归深度为n。

京公网安备 11010502036488号