我遇到一个问题,经过一些修改后会减少到"在[l,r]"
范围内找到数字大于x的最小索引例如:假设一个数组A = {1, 2, 3, 6, 9, 8, 4, 3, 7, 6, 2}
查询是" 在范围[2,6]中找到数组A中元素的最小索引,大于或等于5 "
上述查询的答案是4(此索引的值为6)(指数基于1)
有多个查询,数组未排序(考虑输入已经在内存中)
是否存在可在O(logN)中进行查询的算法,其中N为否。数组A中的元素。
答案 0 :(得分:5)
在构建一个占用O(N)空间的数据结构后,实际上有很多方法可以在O(log N)时间内支持查询。
为了使上述算法真正有效,您可以将树编码为数组,就像我们对堆进行编码一样。在此表示中(使用基于1的索引),您有一个包含N-1个内部节点的最大值的数组,后面依次是N个叶子。调用该数组H
。然后H[i]
的孩子在H[i*2]
和H[i*2+1]
。 H[i]
的父级位于H[i>>1]
在伪代码中,使用基于1的索引,我们得到:
A[] = input array, N = input array size
我们像这样建立H:
H = new array with size N*2-1, indexed from 1 to N*2-1
for (int i=1; i<=N; ++i)
H[i+N-1]=A[i];
for (int i=N-1; i>0; --i)
H[i] = max(H[2*i],H[2*i+1]);
请注意,我们会在父母之前创建子项,以便在我们需要获取其最大值时,孩子们就在那里。
现在,查询功能:
//get the index of the first element with val >= minval, index >= minindex, and index <= maxindex
//returns -1 if there is no such element
firstAtLeast(minval, minindex, maxindex)
if (maxindex < minindex)
return -1;
node = minindex+N-1; //find minindex in the tree
//go up and right until we find a subtree that has a value >= minval
while(H[node] < minval)
//if we are a right child of our parent, go up until
//we have a right sibling
while( (node&1) == 1 ) //node is odd
node = node>>1; //same as floor(node/2);
if (node <= 1)
//we went up to the root
//there is no such element
return -1;
//now node is a left child. try its right sibling
++node;
//We found a subtree. get the first valid leaf
while(node < N) //while it's an internal node
node = 2*N; //left child
if (H[node] < minval)
++node; //left child not valid - move to right child
//Found leaf. get index in A[i] and check against maxindex
index = node-(N-1);
return (index <= maxindex ? index : -1);
这满足O(log N)时间内查询的要求。当你知道那里的答案不会小于maxindex
时,提前退出会很好(而且不是太难),但这会使伪代码变得不那么清晰,所以我会这样做把它留作练习
答案 1 :(得分:4)
O(logN)接缝是不可能的。您至少需要读取输入,直到第一个元素更大(这可能是最后一个元素或根本没有)。因此,在最坏的情况下,您需要读取整个输入,这意味着 O(N)。
只有在您输入的某些额外结构(例如已排序)时才能进行改进,而不是将算法改进为 O(logN)。
如果有多个查询,您仍需要 O(logN)。您可以一次检查多个查询,也可以缓存相同查询再次出现的结果。
答案 2 :(得分:1)
如果可能元素的数量很小(比如K)并且可以很容易地枚举,对于N个元素的数组,您可以使用counting sort按N + K顺序对它们进行排序。然后,您可以对查询使用二进制搜索,这将是订单日志N.注意,计数排序还需要订单K内存,因此仅在相对较少数量的离散键正在运行时才有用。如果您有Q查询,则复杂度为O((N + K)+ Q(log(N))
答案 3 :(得分:0)
如果您有大量查询,并且数据量适中,那么您可以通过O(N)额外存储来提高查询速度。
创建元组(a[i], i)
的aray(即数组中的值,该值的索引),按第一个(以及在冲突的情况下,第二个)按升序排序。然后,使用二进制搜索找到您的起点。如果索引超出了您的范围,请继续遍历您的排序列表,直到找到一个符合您感兴趣范围的索引。
我怀疑这个算法是O(N),最坏的情况,所以我猜有可能做得更好。