Я смотрю на два решения, чтобы найти первого общего предка двух узлов в двоичном дереве (не обязательно в двоичном дереве поиска). Мне сказали, что второе решение обеспечивает лучшее время выполнения в худшем случае, но я не могу понять, почему. Может кто-нибудь, пожалуйста, просветите меня?
Решение 1:
- Найдите глубину каждого из двух узлов: p, q
- Рассчитать дельту их глубин
- Установить указатель на более мелкий узел, указатель на более глубокий узел
- Переместить указатель более глубокого узла вверх по дельте, чтобы мы могли начать движение с той же высоты
- Рекурсивно посещать узлы частей обоих указателей, пока мы не достигнем того же узла, который находится вне первого общего предка
import com.foo.graphstrees.BinaryTreeNodeWithParent;
/*
Find the first common ancestor to 2 nodes in a binary tree.
*/
public class FirstCommonAncestorFinder {
public BinaryTreeNodeWithParent find(BinaryTreeNodeWithParent p, BinaryTreeNodeWithParent q) {
int delta = depth(p) - depth(q);
BinaryTreeNodeWithParent first = delta > 0 ? q: p; // use shallower node
BinaryTreeNodeWithParent second = delta > 0 ? p: q; //use deeper
second = goUp(second, delta); // move up so they are level, if 1 node is deeper in the tree than the other, their common ancestor obviously cannot be below the shallower node, so we start them off at the same height in the tree
//keep going up the tree, once first == second, stop
while(!first.equals(second) && first !=null && second !=null) {
first = first.getParent();
second = second.getParent();
}
return first == null || second == null ? null : first;
}
private int depth(BinaryTreeNodeWithParent n) {
int depth = 0;
while (n != null) {
n = n.getParent();
depth++;
}
return depth;
}
private BinaryTreeNodeWithParent goUp(BinaryTreeNodeWithParent node, int delta) {
while (delta > 0 && node != null) {
node = node.getParent();
delta--;
}
return node;
}
}
Решение 2:
- Убедитесь, что оба дерева (p, q) существуют в дереве, начиная с корневого узла
- Убедитесь, что q не является потомком p и p не является потомком q, пройдя через их поддеревья
- Рекурсивно исследовать поддеревья последовательных родительских узлов p, пока не будет найдено q
import com.foo.graphstrees.BinaryTreeNodeWithParent;
public class FirstCommonAncestorImproved {
public BinaryTreeNodeWithParent find(BinaryTreeNodeWithParent root,
BinaryTreeNodeWithParent a,
BinaryTreeNodeWithParent b) {
if (!covers(root, a) || !covers(root, b)) {
return null;
} else if (covers(a, b)) {
return a;
} else if (covers(b, a)) {
return b;
}
var sibling = getSibling(a);
var parent = a.getParent();
while (!covers(sibling, b)) {
sibling = getSibling(parent);
parent = parent.getParent();
}
return parent;
}
private BinaryTreeNodeWithParent getSibling(BinaryTreeNodeWithParent node) {
if (node == null || node.getParent() == null) return null;
var parent = node.getParent();
return node.equals(parent.getLeft()) ? node.getRight() : node.getLeft();
}
private boolean covers(BinaryTreeNodeWithParent root,
BinaryTreeNodeWithParent node) {
if (root == null) return false;
if (root.equals(node)) return true;
return covers(root.getLeft(), node) || covers(root.getRight(), node);
}
}