我一直在阅读有关the Lowest Common Ancestor algorithm on top coder的内容,我无法理解为什么要使用RMQ算法 - 这里列出的解决方案非常复杂并具有以下属性:
我的解决方案:给定2个整数值,通过简单的前序遍历找到节点。获取其中一个节点并上升树并将路径存储到Set中。取另一个节点,然后上升树并检查每个节点:如果节点在Set中,则停止并返回LCA。 Full implementation
所以考虑到这两个选择,Top Coder上的算法是否更好?如果是,为什么?这是我无法理解的。我认为O(log n)优于O(sqrt(n))。
public class LCA {
private class Node {
int data;
Node[] children = new Node[0];
Node parent;
public Node() {
}
public Node(int v) {
data = v;
}
@Override
public boolean equals(Object other) {
if (this.data == ((Node) other).data) {
return true;
}
return false;
}
}
private Node root;
public LCA() {
root = new Node(3);
root.children = new Node[4];
root.children[0] = new Node(15);
root.children[0].parent = root;
root.children[1] = new Node(40);
root.children[1].parent = root;
root.children[2] = new Node(100);
root.children[2].parent = root;
root.children[3] = new Node(10);
root.children[3].parent = root;
root.children[0].children = new Node[3];
root.children[0].children[0] = new Node(22);
root.children[0].children[0].parent = root.children[0];
root.children[0].children[1] = new Node(11);
root.children[0].children[1].parent = root.children[0];
root.children[0].children[2] = new Node(99);
root.children[0].children[2].parent = root.children[0];
root.children[2].children = new Node[2];
root.children[2].children[0] = new Node(120);
root.children[2].children[0].parent = root.children[2];
root.children[2].children[1] = new Node(33);
root.children[2].children[1].parent = root.children[2];
root.children[3].children = new Node[4];
root.children[3].children[0] = new Node(51);
root.children[3].children[0].parent = root.children[3];
root.children[3].children[1] = new Node(52);
root.children[3].children[1].parent = root.children[3];
root.children[3].children[2] = new Node(53);
root.children[3].children[2].parent = root.children[3];
root.children[3].children[3] = new Node(54);
root.children[3].children[3].parent = root.children[3];
root.children[3].children[0].children = new Node[2];
root.children[3].children[0].children[0] = new Node(25);
root.children[3].children[0].children[0].parent = root.children[3].children[0];
root.children[3].children[0].children[1] = new Node(26);
root.children[3].children[0].children[1].parent = root.children[3].children[0];
root.children[3].children[3].children = new Node[1];
root.children[3].children[3].children[0] = new Node(27);
root.children[3].children[3].children[0].parent = root.children[3].children[3];
}
private Node findNode(Node root, int value) {
if (root == null) {
return null;
}
if (root.data == value) {
return root;
}
for (int i = 0; i < root.children.length; i++) {
Node found = findNode(root.children[i], value);
if (found != null) {
return found;
}
}
return null;
}
public void LCA(int node1, int node2) {
Node n1 = findNode(root, node1);
Node n2 = findNode(root, node2);
Set<Node> ancestors = new HashSet<Node>();
while (n1 != null) {
ancestors.add(n1);
n1 = n1.parent;
}
while (n2 != null) {
if (ancestors.contains(n2)) {
System.out.println("Found common ancestor between " + node1 + " and " + node2 + ": node " + n2.data);
return;
}
n2 = n2.parent;
}
}
public static void main(String[] args) {
LCA tree = new LCA();
tree.LCA(33, 27);
}
}
答案 0 :(得分:3)
LCA算法适用于任何树(不一定是二进制的,不一定是平衡的)。由于跟踪到根节点的路径实际上是O(N)时间和空间而不是O(log N)
,因此您的“简单算法”分析会中断答案 1 :(得分:2)
只是想指出问题是关于root树而不是二叉搜索树。所以,在你的算法中
在给定值的情况下,找到每个2个节点的O(n)时间复杂度 用于将路径存储到Set中的O(n)空间复杂度 使用第二个节点上升树并搜索前n个存储元素时的O(sqrt(n))时间复杂度。当我们从第二个节点上升时检查每个节点,取O(n),因此对于n个节点,它将需要O(sqrt(n))。
答案 2 :(得分:1)
Harel和Tarjan LCA算法(在您提供的链接中引用)使用具有O(n)复杂度的预计算,之后查找为O(1)(不是O(您声称的sqrt(n))