Fenwick树确定一个点落入哪个区间

时间:2016-01-09 22:20:04

标签: algorithm tree

0 ,..., n-1 是一系列长度。我们可以构造区间[0,a 0 ],(a 1 ,a 2 + a 1 ], (一<子> 2 +一<子> 1 ,一个<子> 3 +一<子> 2 +一<子> 1 ],...我将序列a 1 ,..., n-1 存储在Fenwick树中。

我问这个问题:给定一个数m,我怎样才能有效地(记录时间)找到m落入的区间?

例如,给出a:3,5,2,7,9,4。

芬威克树存储3,8,2,17,9,13。

区间为[0,3],(3,8),(8,10),(10,17),(17,26),(26,30)。

给定数字9,算法应该返回Fenwick树的第3个索引(如果使用基于0的数组,则返回2个,如果使用基于1的数组,则返回3个)。给定数字26,算法应该返回Fenwick树的第5个索引(如果使用基于0的数组则为4个,如果使用基于1的数组则为5个)。

可能另一种数据结构可能更适合此操作。我使用的是Fenwick Trees,因为它们看似简单而且效率高。

2 个答案:

答案 0 :(得分:1)

我们可以进行O(log n)时间搜索操作。诀窍是将二进制搜索与前缀sum操作集成。

def get_total(tree, i):
    total = 0
    while i > 0:
        total += tree[i - 1]
        i -= i & (-i)
    return total


def search(tree, total):
    j = 1
    while j < len(tree):
        j <<= 1
    j >>= 1
    i = -1
    while j > 0:
        if i + j < len(tree) and total > tree[i + j]:
            total -= tree[i + j]
            i += j
        j >>= 1
    return i + 1


tree = [3, 8, 2, 17, 9, 13]
print('Intervals')
for i in range(len(tree)):
    print(get_total(tree, i), get_total(tree, i + 1))
print('Searches')
for total in range(31):
    print(total, search(tree, total))

输出

Intervals
0 3
3 8
8 10
10 17
17 26
26 30
Searches
0 0
1 0
2 0
3 0
4 1
5 1
6 1
7 1
8 1
9 2
10 2
11 3
12 3
13 3
14 3
15 3
16 3
17 3
18 4
19 4
20 4
21 4
22 4
23 4
24 4
25 4
26 4
27 5
28 5
29 5
30 5

答案 1 :(得分:0)

如果间隔不经常更改,您可以在累积数组中使用简单的二进制搜索来执行此操作。在Python中,您可以使用bisect模块来执行此操作。每个查询都是O(log n):

import bisect

A = [3, 5, 2, 7, 9, 4]

for i in xrange(1, len(A)):
    A[i] += A[i-1]

print bisect.bisect_left(A, 9)
print bisect.bisect_left(A, 26)

如果间隔发生变化,您可以使用相同的想法,但每个数组查找都将为O(log n),从而使查询操作O(log²n)整体。