最低的共同祖先

时间:2014-04-10 13:46:17

标签: algorithm optimization tree binary-tree

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

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

我查看LCA using RMQ,但我不能使用该技术,因为我不能在树中使用这个节点的数组。

有些人能够快速有效地实现快速回答许多查询的算法,知道它是完整的二叉树,节点之间的关系如上所述。

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

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

2 个答案:

答案 0 :(得分:5)

如果用二进制表示两个索引,则可以通过两个步骤找到LCA:

  1. 向右移动较大的数字,直到前导1位在 与其他号码相同的地方。
  2. 向右移两个数字直到它们相同。
  3. 第一步可以通过获取数字的日志基数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)
    

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

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

答案 1 :(得分: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&amp;使用二进制搜索的y是O(log(32))
  2.   
  3. x&amp;的二进制表示法的公共前缀y是共同的祖先
  4.   
  5. 由较大的比特数表示的任何一个由k>&gt;带到相同的比特。 DIFF
  6.   
  7. k = x ^ y擦除x&amp;的公共前缀ÿ
  8.   
  9. 找到代表剩余后缀的位
  10.   
  11. 通过后缀位移动x或y以获得共同前缀,这是共同的祖先。
  12.   

示例: -

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