二叉树之(最近)公共祖先
思路:
考虑问题一个简单的问题:如何寻找一个或多个元素:
Eg: 寻找值为 val1
或 val2
的节点
非常简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| TreeNode find(TreeNode root, int val1, int val2) { if (root == null) { return null; } if (root.val == val1 || root.val == val2) { return root; } TreeNode left = find(root.left, val1, val2); TreeNode right = find(root.right, val1, val2);
return left != null ? left : right; }
|
那么对于寻找LCA来说,其实就是找 在一个子树下面是否能够同时找到p和q,寻找最近的公共节点就是寻找最深的公共节点,因此要选择后序遍历
那么我们就会得出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { return findTargeValues(root, p, q); }
private TreeNode findTargeValues(TreeNode root, TreeNode p, TreeNode q) { if (root == null) return null;
if (root.val == p.val || root.val == q.val) { return root; }
TreeNode leftValue = findTargeValues(root.left, p, q); TreeNode rightValue = findTargeValues(root.right, p, q);
if (leftValue != null && rightValue != null) { return root; }
return leftValue == null ? rightValue : leftValue; }
|
注意
leftValue == null ? rightValue : leftValue
就是
1 2 3 4 5 6 7 8 9
| if(left == null && right == null) { return null; }else if(left == null && right != null) { return right; }else if(left != null && right == null) { return left; }else { return root; }
|
本题 不再是p,q,而是一组node
只需要稍微修改一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public TreeNode lowestCommonAncestor(TreeNode root, TreeNode[] nodes) { Set<Integer> hs = new HashSet<>(); for (TreeNode node : nodes) { hs.add(node.val); } return findTargetsFromNodes(root, hs); }
private TreeNode findTargetsFromNodes(TreeNode root, Set<Integer> nodes) { if (root == null) { return null; }
if (nodes.contains(root.val)) { return root; }
TreeNode leftValue = findTargetsFromNodes(root.left, nodes); TreeNode rightValue = findTargetsFromNodes(root.right, nodes);
if (leftValue != null && rightValue != null) { return root; }
return leftValue != null ? leftValue : rightValue; }
|
之前的题目都说target节点都在树里,但是本题 不保证树里一定有p 和 q
那么,对于上边两道题在前序遍历位置时发现当前root节点和target值相等直接返回就不能用了,因为不能保证是不是另一个也存在,我们需要知道树的所有信息才行,也就是要在后序遍历的位置,即:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| boolean isPExist = false, isQExist = false; public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { TreeNode ret = findTargetValues(root, p, q); if (!isQExist || !isPExist) return null; return ret; }
private TreeNode findTargetValues(TreeNode root, TreeNode p, TreeNode q) { if (root == null) return null;
TreeNode leftValue = findTargetValues(root.left, p, q); TreeNode rightValue = findTargetValues(root.right, p, q);
if (leftValue != null && rightValue != null) { return root; }
if (root.val == p.val) { isPExist = true; return root; }
if (root.val == q.val) { isQExist = true; return root; } return leftValue == null ? rightValue : leftValue; }
|
本题最重要的是利用BST的性质
- 当根节点值小于p和q 即 小于 p,q的最小值
- 说明要的公共祖先在右边的树里
- 当根节点值大于p和q 即 大于 p,q的最大值
- 说明要的公共祖先在左边的树里
- 当根节点介于中间时则一定为最近的公共祖先
- 为什么最近?
- 因为如果还要进行下去,要么往左走要么往右走总会丢失一个
- 按图中的例子,如果5下面还有更近的祖先,那么假设5往左走,那么会丢失q,同理,向右走则会丢失p
结论: Math.min(q.val, p.val) <= root.val <= Math.max(q.val, p.val)
那么此时的root即为最近的公共祖先
pseudocode:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { int val1 = Math.min(p.val, q.val); int val2 = Math.max(p.val, q.val); return findTarget(root, val1, val2); }
private TreeNode findTarget(TreeNode root, int val1, int val2) { if (root == null) return null; if (root.val < val1) { return findTarget(root.right, val1, val2); }
if (root.val > val2) { return findTarget(root.left, val1, val2); } return root; }
|
本题包含了对父节点的引用:
1 2 3 4 5 6
| class Node { public int val; public Node left; public Node right; public Node parent; }
|
这道题用的是链表合并的思路:
即让两个指针p1 指向A的指针,p2指向B的指针,能够同时到达相交节点 c1
用两个指针 p1
和 p2
分别在两条链表上前进,我们可以让 p1
遍历完链表 A
之后开始遍历链表 B
,让 p2
遍历完链表 B
之后开始遍历链表 A
,这样相当于「逻辑上」两条链表接在了一起。
稍微替换一下,借用parent:
1 2 3 4 5 6 7 8 9 10
| public Node lowestCommonAncestor(Node p, Node q) { Node pPointer = p, qPointer = q; while (pPointer != qPointer) { if (pPointer == null) pPointer = q; else pPointer = pPointer.parent; if (qPointer == null) qPointer = p; else qPointer = qPointer.parent; } return pPointer; }
|