前言
二叉树问题都可分类于,前序与dfs/中序与平衡二叉/后序与回溯/层序与bfs。二叉树最近的公共祖先,用回溯时寻找到的祖先体现为最近祖先。
一、二叉树最近的公共祖先
二、直接法 & 回溯模型
1、直接法
按后序遍历的节点顺序,以每个节点作为root,寻找左右节点,当两个都找到了则该root为最近公共祖先。
// 二叉树的最近公共祖先。
public class LowestCommonAncestor {
// 按后序遍历的节点顺序,以每个节点作为root,寻找左右节点,当两个都找到了则该root为最近公共祖先。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) return null;
TreeNode left = lowestCommonAncestor(root.left, p, q);
if (left != null) return left;
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (right != null) return right;
flag1 = flag2 = false;
order(root, p, q);
return flag1 && flag2 ? root : null;
}
boolean flag1, flag2;// 标记两个是否都找到了。
private void order(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) return;
if (root == p) flag1 = true;
if (root == q) flag2 = true;
order(root.left, p, q);
order(root.right, p, q);
}
// Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
}
2、回溯模型(合并if)
利用回溯时判定左右两边是否都找到了。(后序遍历与回溯模型的考察。)
两种情况,p/q有一个作为它们俩的最近公共祖先;有个root的左右子树上挂着p/q;
第一种情况:率先找到那个就是它俩公共祖先;第二种情况:回溯时左右子树都找到了,返回root即可。
注:左子树找到了root,就不用再遍历右边的所有右子树了,这部分可以剪枝。
// 利用回溯时判定左右两边是否都找到了。(后序遍历与回溯模型的考察。)
// 两种情况,p/q有一个作为它们俩的最近公共祖先;有个root的左右子树上挂着p/q;
// 第一种情况:率先找到那个就是它俩公共祖先;第二种情况:回溯时左右子树都找到了,返回root即可。
// 注:左子树找到了root,就不用再遍历右边的所有右子树了,这部分可以剪枝。
class LowestCommonAncestor2 {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || isFind) return null;// 剪枝。
if (root == p || q == root) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 一个在左,一个在右,root就是最近公共祖先。
if (left != null && right != null) {
isFind = true;
return root;
}
return left != null ? left : right;
}
boolean isFind = false;// 找到了设个标记,方便剪枝。
// Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
总结
1)二叉树问题,莫过于前序与dfs/中序与平衡二叉/后序与回溯/层序与bfs。
2)9个月前写的回溯模型,回溯时有left/right是否为null的四种情况判定,意味着4个if,现在很容易就把if合并,写出更简洁的代码,所以要多练才会灵活,思路才能打开 & 寻找到联系之处。