计算巨大/大数组中的不同元素的数量

时间:2013-12-14 12:56:56

标签: performance algorithm data-structures

我有一个大数组说..数组[7000]。每个索引都有一个介于0和9之间的数字。然后给出两个索引说x = 100,y = 105我需要找到之间不同元素的数量给定的范围。 假设

array[100]=1;
array[101]=2;
array[102]=1;
array[103]=4;
array[104]=7;
array[105]=2;

现在这里不同元素的数量是4 {即数字1,2,1,7}。 这可以通过在两个索引之间迭代并跟踪数字来计算。但是我面临的主要问题是程序必须执行的查询数量非常大,即操作必须完成几千个x和y的值(比如10 ^ 6个查询),在这种情况下,这个方法会变得安静无效。可能是一个更好的算法,我试着在很多地方搜索它。

4 个答案:

答案 0 :(得分:5)

好的,这个解决方案不是内存效率,但它可以用来计算O(1)中的查询。

形成另一个7000x10的2D数组,其中每一行都是该条目的[0到9s]的计数。

因此,无论何时获得查询,您只需要减去行的相应索引以获得[0..9]数字的计数。

检查哪些指数具有非零计数差异。

例如,数组行可能如下所示:

A[36] = { 5, 4, 2,  1, 4, 3, 7, 8, 2, 1 }
A[38] = { 6, 4, 2,  1, 4, 3, 7, 8, 2, 2 }

Diff =  { 1, 0, 0,  0, 0, 0, 0, 0, 0, 1 }

因此在36和38之间有两个独特的元素(0和9)。

例2:
对于输入数组{1,2,3,2,4},矩阵A应为:

A[0] = { 0, 1, 0,  0, 0, 0, 0, 0, 0, 0 }
A[1] = { 0, 1, 1,  0, 0, 0, 0, 0, 0, 0 }
A[2] = { 0, 1, 1,  1, 0, 0, 0, 0, 0, 0 }
A[3] = { 0, 1, 2,  1, 0, 0, 0, 0, 0, 0 }
A[4] = { 0, 1, 2,  1, 1, 0, 0, 0, 0, 0 }

因此对于A [3]到A [4],应该有1个唯一元素(4)

Diff =  { 0, 0, 0,  0, 1, 0, 0, 0, 0, 0 }

因此对于A [1]到A [4],应该有3个唯一元素(2,3,4)

Diff =  { 0, 0, 1,  1, 1, 0, 0, 0, 0, 0 }

编辑:我已经考虑过A [1]到A [4]的含义是指数[2到4]中的唯一元素。如果需要,可以将其更改为1到4。在那种情况下,

Diff =  { 0, 0, 2,  1, 1, 0, 0, 0, 0, 0 }

(同样有3个独特元素)

答案 1 :(得分:2)

您正在寻找分段树。

http://en.wikipedia.org/wiki/Segment_tree

从每个索引的值介于0-1之间的问题开始,然后逐步提升。您可以使用O(logn)时间回答查询,并使用O(nlogn)时间构建树。

答案 2 :(得分:2)

由于你的瓶颈是查询时间,而数组元素是0-9,这是我的解决方案,只有O(n)。

Assume:
A = arr
P = {} (Hash or Dict)
P[A,0,-1] = [0,0,0,0,0,0,0,0,0,0]   

For i = 0,len(A):
    e = A[i]
    P[A,0,i] = P[A,0,i-1] where P[A,0,i-1][e]++ 

Query(A,x,y)
    res = 0
    ax = P[A,0,x]
    ay = P[A,0,y]
    for i = 0,10
        res += ay[i] - ax[i] > 1 ? 1 : ay[i] - ax[i]    

    return res + 1

A = [1,2,1,4,7,2,2]

P [A,0,0] = [0,1,0,0,0,0,0,0,0,0]

P [A,0,1] = [0,1,1,0,0,0,0,0,0,0]

P [A,0,2] = [0,2,1,0,0,0,0,0,0,0]

P [A,0,3] = [0,2,1,0,1,0,0,0,0,0]

P [A,0,4] = [0,2,1,0,1,0,0,1,0,0]

P [A,0,5] = [0,2,2,0,1,0,0,1,0,0]

P [A,0,6] = [0,2,3,0,0,0,0,0,0,0]

查询(A,1,4)= [0,1,0,1,0,0,0,0,1,0,0] = 3 + 1 = 4

查询(A,4,6)= [0,0,2,0,0,0,0,0,0,0,0] = [0,0,1,0,0,0,0 ,0,0,0,0] = 1 + 1 = 2

答案 3 :(得分:1)

由于数字在0-9范围内,我们可以使用一些不错的作弊。

主要思想:在整数中使用位i来表示存在数字i。所以{}变为0,{0}变为1,{1}变为2,{0,1}变为3,依此类推。向x添加项set | (1 << x)很容易,只需要set1 | set2,类似地,两个这样的集合的联合是微不足道的:(1 << array[i]) | (1 << array[i + 1])。还有一种很好的方法来计算整数中1位的数量,这给出了集合的基数。

这无济于事,它只是改善了查询的真实性能。但这不是全部。

现在构建一个二叉树,在叶子处以x .. y为节点。在上面的级别,你采取两个孩子的联合(如果一个孩子不在场,只需要考虑另一个孩子的价值)。这棵树是一个完整的二叉树,所以你应该像binary heap那样存储它(当然它不是堆),以避免在子指针上浪费大量空间。这样,您只需要在不超过输入的数组中存储表示集合的整数。构建此树显然需要O(n)时间。

对于范围1 << array[something]的查询,取该范围内所有内容的并集*(注意边缘情况),然后计算最终集合的population count(可以稍微优化一下)因为只能设置10位)。这是一个O(log(y - x))操作。

*:要稍微扩展一下,想法是递归树,并且只要范围完全包含当前节点,就返回该节点。如果叶子未完全包含在范围内,请取
{{1}}(来自输入数组)。