求二叉树中两个节点 的最近公共祖先。
注意,图中的所有节点唯一。如节点5和4的公共祖先节点为5,1和5的公共祖先节点为3。
在做了一些二叉树的题后,本能的使用了递归的做法写出了代码并调试通过了。虽然通过了,但是还是对其细节不太清楚。 此处再通过代码来理解其中的思想并记录。如下
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return root;
}
// root 等于p 或 q中的任一个,那么公共的祖先就是此节点。
if (root == p) {
return p;
}
if (root == q) {
return q;
}
// 搞懂原理后,以上三个if条件可以合并成一个。 均返回root即可。
// 递归在左子树中找祖先。当p与q不同在root.left的子树中时,返回的必然是 p 或 q 中的一个(由上面的if条件可知)。 注意,要理解这个。
TreeNode left = lowestCommonAncestor(root.left, p, q);
// 同理,在右子树中找祖先。
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 而如果p 或 q 同在root.left上,那么right 即为null。反之亦然。
// left与right不等,说明 p 与 q 既不同在root.left,也不同在root.right。而是分布在root的左右两棵子树中。所以root是公共祖先。
// 如果相等(相等的时候,left与right都为null,因为公共祖先不可能同时在左子树和右子树中找到), 此时没有公共祖先,返回null。
return left != right ? root : left;
}
在上面基础上简化出另一种形式的代码如下:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == q || root == p) {
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 分布在root的左右子树两侧。
if (left != null && right != null) {
return root;
}
return left == null ? right : left;
}
如果在修改此题为查找二叉搜索树中两节点的公共祖先,则可以通过二叉搜索树的性质简化代码如下:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (p.val > root.val && q.val > root.val) {
// 如果 p 和 q 均处在右子树,那么只在右子树中找
return lowestCommonAncestor(root.right, p, q);
} else if (p.val < root.val && q.val < root.val) {
// 同理,只在左子树中查找。
return lowestCommonAncestor(root.left, p, q);
} else {
// 当p,q分别在root节点的两侧,那么此时root就是公共祖先了,故此时返回结果。
}
return root;
}
用迭代的方式如下:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while (root != null) {
// 在右子树上
if (root.val < p.val && root.val < q.val) {
root = root.right;
} else if (root.val > q.val && root.val > p.val) {
// 同理
root = root.left;
} else {
break;
}
}
return root;
}
如果事先确定p与q的大小,则可以减少while中的条件比较。 如下:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (p.val > q.val) {
TreeNode tmp = p;
p = q;
q = tmp;
}
// 保证节点q的值大
while (root != null) {
// 小的比root大,则大的一定比root大
if (root.val < p.val) {
root = root.right;
} else if (root.val > q.val) {
// 同理
root = root.left;
} else {
break;
}
}
return root;
}
因为极端条件下要遍历所有的节点,故以上算法时间复杂度为O(N)。