fractional cascading

算法

问题:有k个长度分别为L1, L2, L3 … Lk的数组,每次询问一个数q,求每个数组里大于等于q的最小的数。

如果只有一个数组,那么只需要对数组排序,然后二分即可,复杂度O(L)。

对于k个数组的情况,直接二分k次。设总元素个数是n, 最坏复杂度就是O(k*log(n / k)),此时每个数组元素个数相等。

fractional cascading(以下简称FC)是用来处理这种的询问的算法,单次询问时间复杂度是O(k + log n),空间复杂度O(n)。


FC的本质思想实际上类似线段树(或者排序二叉树),在一个数组中利用线段树查询需要O(L)的空间和O(logL)的时间。线段树的每个节点只要存储子树的中位数即可。将这个思路扩展到多个数组就可以得到FC。


对于如下数据

L1 = 24, 64, 65, 80, 93
L2 = 23, 25, 26
L3 = 13, 44, 62, 66
L4 = 11, 35, 46, 79, 81

FC构造了一个表:

M1 = 24[0, 1], 25[1, 1], 35[1, 3], 64[1, 5], 65[2, 5], 79[3, 5], 80[3, 6], 93[4, 6]
M2 = 23[0, 1], 25[1, 1], 26[2, 1], 35[3, 1], 62[3, 3], 79[3, 5]
M3 = 13[0, 1], 35[1, 1], 44[1, 2], 62[2, 3], 66[3, 3], 79[4, 3]
M4 = 11[0, 0], 35[1, 0], 46[2, 0], 79[3, 0], 81[4, 0] 

Mi包含Li的所有元素,以及所有Mi+1下标为奇数(下标从零开始)的元素。

对于Mi的每个元素,需要存两个值,第一个是在Li查询该元素得到的位置,第二个是在Mi+1中查询该元素得到的位置。

在上表查询40的过程如下:

  1. 首先在M1二分,得到元素64,根据64记录的第一个值得到L1中的位置是1.
  2. 根据64记录的第二个值,在M2的位置是79,在L2的3(表尾的话需要特殊处理一下),同时还需要考虑前一个数(62)的情况,因为查询的数40在L1查询的结果只可能是这两个数在L1查询得到的位置。此时62符合情况。
  3. 到了M3的3,重复上面的过程,找到62。与前一个数44比较,答案是44.
  4. 在M4找到46

时间复杂度只需要在M1做一次二分,然后在后面的k - 1个表里都是O(1)的。

空间复杂度,由于每个表的元素在上一层至少会减半(相同元素需要合并),所以每个表Li的元素占用的空间是Li + Li / 2 + Li / 4 …..,显然这个求和结果是O(Li)的。查询的过程与线段树非常相似,只是存储方式有所不同。

reference

wiki

Post Directory

文章目录

  1. reference