查找范围内大于x的数字

时间:2016-03-07 11:03:14

标签: algorithm

我遇到一个问题,经过一些修改后会减少到"在[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中的元素。

4 个答案:

答案 0 :(得分:5)

在构建一个占用O(N)空间的数据结构后,实际上有很多方法可以在O(log N)时间内支持查询。

易于理解的答案

  • 制作一个二元树,其中A元素为叶子,按索引排序。
  • 在每个内部节点中,记录树叶在其子树中的最大值
  • 您需要能够在给定索引的情况下找到节点的路径。如有必要,记录每个内部节点中第一个叶子的索引。没有这个,你可以通过建造一个方便形状的树来逃脱。
  • 现在,要找到值> gt = X的最小索引> = L:
    • 在树中找到A [L]
    • 的路径
    • 如果A [L]< X,然后上到树,直到找到一个包含值> = X
    • 的右叔叔
    • 沿着叔叔树走下去,找到值为> = X的第一片叶子。在下降时,如果左孩子的叶子>> X(检查存储的最大值),则向左移动。否则就走吧。

超高效答案

为了使上述算法真正有效,您可以将树编码为数组,就像我们对堆进行编码一样。在此表示中(使用基于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),最坏的情况,所以我猜有可能做得更好。