我有一个基本数组,其元素为[0到 N - 1],其中每个元素都是一个索引总是指向数组中较早位置的结构
有一次,作为更大算法的一部分,我想在节点 X 和之后的任何节点之间找到特定的 C 最低共同祖先。
int LCA(a, b) {
while (a != b) {
if (a > b) {
a = nodes[a].parent;
} else {
b = nodes[b].parent;
}
}
return a;
}
for (y = x + 1; y < n; ++y) {
if (LCA(x, y) == c) {
//other code
}
}
上面的代码实际上是伪代码。我已经设法通过在使用时生成查找表来略微提高LCA()的性能。像这样:
int LCA(a, b) {
if (lookup[a, b]) {
return lookup[a, b];
}
oa = a; ob = b;
while (a != b) {
if (a > b) {
a = nodes[a].parent;
} else {
b = nodes[b].parent;
}
}
lookup[oa, ob] = a;
lookup[ob, oa] = a;
return a;
}
我知道我可以通过某种方式制作某种专门的LCA()函数,即以某种方式替换所有上述代码以使其专门化,因此速度更快。但我没有想到任何有趣的东西。
我试图通过查看是否LCA(c, y) == LCA(x, y)
来查看是否可以在 C 和 Y 之间进行LCA检查,但当然不是准确。
重新上限: X 始终小于 Y 。 C 始终小于 X (因此 Y )。父母的指数总是低于他们的孩子(所以它是订购的)。
节点知道它们的深度会有帮助吗?
此代码占整个算法的CPU时间的80%,总共需要4分钟。对此的解决方案将容易地改进整个算法。谢谢!
答案 0 :(得分:4)
LCA
和x
的{{1}}将是y
出现与x
出现之间y
的最小高度的节点树的3}}(*)。要在O(1)
时间内找到此信息,您需要使用euler tour解决RMQ problem。
(*):您的旅程需要稍作修改才能正常工作。每次返回时都必须为数组追加一个值(从递归调用返回到子节点)。对于维基树,它看起来像这样:
1 2 3 4 5 6 7 8 9 10 11
1 2 6 2 4 2 1 3 1 5 1
请注意,有两次显示叶子没有意义(尽管它不会影响正确性)。
因此,例如,RMQ(2, 5)
将是具有最小高度的节点:
2 3 4 5 6 7 8 9 10
2 6 2 4 2 1 3 1 5
哪个是节点1
。
这不是您可以采取的唯一有效间隔。最后一次出现2
:
6 7 8 9 10
2 1 3 1 5
这也将1
作为LCA
返回。
这样,您可以在固定时间内回答LCA
个查询,并在预处理上花费线性时间。