【问题标题】:Lowest Common Ancestor最低共同祖先
【发布时间】:2014-05-24 06:27:27
【问题描述】:

给定完整二叉树中的两个节点(父 x 比子 2*x 和 2*x+1),我正在寻找最低共同祖先的恒定时间实现。

我的问题是树中有大量节点和很多查询。是否有一种算法可以进行预处理,以便可以在恒定时间内回答查询。

我查看了LCA using RMQ,但我无法使用该技术,因为我无法将数组用于树中的这么多节点。

知道它是完整的二叉树并且节点之间的关系如上所述。

我所做的是从两个给定节点开始,并依次找到它们的父节点 (node/2),保留访问节点的哈希列表。当我们到达一个已经在哈希列表中的节点时,该节点将是最低的共同祖先。

但是当有很多查询时,这个算法非常耗时,因为在最坏的情况下,我可能必须遍历 30 的高度(树的最大高度)才能到达根(最坏的情况)。

【问题讨论】:

  • 请澄清树是如何表示的:听起来它存储在一个数组中,其中索引 X 的节点的子节点位于索引 2*x 和 2*x+1。
  • @ScottHunter 他说这不是一个数组。
  • 不需要存储树,因为节点是按顺序编号的,我们可以得到 2*x 和 2*x+1 的子节点
  • 所以节点按照它们存储在数组中的方式编号。

标签: algorithm optimization tree binary-tree


【解决方案1】:

如果你用二进制表示这两个索引,那么可以分两步找到 LCA:

  1. 将较大的数字右移,直到前导 1 位在 与其他号码相同的位置。
  2. 右移两个数字,直到它们相同。

第一步可以通过获取数字的以 2 为底的对数并将较大的数字右移差来完成:

if a>b:
    a = shift_right(a,log2(a)-log2(b))
else:
    b = shift_right(b,log2(b)-log2(a))

第二步可以通过对得到的两个数字进行异或运算并右移结果的对数基数 2(加 1)来完成:

if a==b:
    return a
else:
    return shift_right(a,log2(xor(a,b))+1)

可以在 O(log(word_size)) 时间内找到对数基数 2,因此只要您使用具有固定位数的整数索引,这实际上是恒定的。

有关计算对数基数 2 的快速方法的信息,请参阅此问题: Fast computing of log2 for 64-bit integers

【讨论】:

  • 我知道有这样的事情;非常好。
  • 这仍然是 O(树的高度),而不是恒定的。虽然它应该比遍历节点更快。如果有一些神奇的方法可以在单个机器指令中获取以 2 为底的日志,而不考虑 O(位数)操作,那么它可以被认为是恒定的唯一方法。
  • @user2566092:是的,也许技术上它不是恒定时间,但是可以在 log(log(n)) 时间内找到 log base 2,对于任何特定的字长,这是一个常数。
  • 我正要添加一个答案,解释如何在 log(位数)操作中获取整数 log base2。您可能希望将其添加到您的答案中。
  • @VaughnCato 我们是否需要对两个数字的对数进行差分并移动原始数字。请澄清,因为那部分不是很清楚。
【解决方案2】:

编辑:-

在 O(log(logn)) 中获取 common_ancestor 的更快方法:-

int get_bits(unsigned int x) {
  int high = 31;
  int low = 0,mid;
  while(high>=low) {
    mid = (high+low)/2;
    if(1<<mid==x)
      return mid+1;
    if(1<<mid<x) {
      low = mid+1;
    }
    else {
      high = mid-1;
    }
  }
  if(1<<mid>x)
    return mid;
  return mid+1;
}

unsigned int Common_Ancestor(unsigned int x,unsigned int y) {

  int xbits = get_bits(x);
  int ybits = get_bits(y);
  int diff,kbits;
  unsigned int k;
  if(xbits>ybits) {
    diff = xbits-ybits;
    x = x >> diff;
  }
  else if(xbits<ybits) {
    diff = ybits-xbits;
    y = y >> diff;
  }
  k = x^y;
  kbits = get_bits(k);
  return y>>kbits;  

}

解释:-

  1. 获取表示 x 和 y 所需的位,使用二进制搜索是 O(log(32))
  2. x & y 二进制符号的共同前缀是共同祖先
  3. 以较大的位数表示的那个被 k 带到同一位 >> diff
  4. k = x^y 擦除 x & y 的公共前缀
  5. 查找表示剩余后缀的位
  6. 将 x 或 y 移动后缀位以获得共同前缀,即共同祖先。

示例:-

x = 12 = b1100 
y = 8  = b1000

xbits = 4
ybits = 4
diff = 0
k = x^y = 4 = b0100
kbits = 3
res = x >> kbits = x >> 3 = 1

ans : 1

【讨论】:

  • 我尝试了同样的事情,但如果根是共同祖先的话会花费很长时间
  • 是的,这是完美的方法,但在上述问题中给出了相同的解决方案。谢谢,你的解释好多了,所以接受你的回答。
  • @TejasPatel 没有注意到 :)
  • 很棒的方法!非常好!
猜你喜欢
  • 1970-01-01
  • 2013-10-01
  • 1970-01-01
  • 2011-05-03
  • 1970-01-01
  • 1970-01-01
  • 2011-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多