Finding a number greater than x in a range

There are actually a bunch of ways to support queries in O(log N) time after building a data structure that takes O(N) space.

Easy to understand answer

  • Make a binary tree with the elements of A as the leaves, ordered by index.
  • In each internal node, record the maximum value of leaves in its subtree
  • You need to be able to find the path to a node given its index. If necessary, record the index of the first leaf in each internal node. You can get away without this by building your tree with a convenient shape.
  • Now, to find the least index >= L with a value >= X:
    • Find the path in the tree to A[L]
    • If A[L] < X, then go up the tree until you find a right uncle that contains a value >= X
    • Go down the uncle tree to find the first leaf with value >=X. While descending, if the left child has a leaf >= X (check the stored max value), then go left. Otherwise go right.

Super-Efficient Answer

To make the above algorithm really efficient, you can encode the tree into an array, like we do for heaps. In this representation (using 1-based indexes), you have an array containing the maximum values for N-1 internal nodes, followed by the N leaves in order. Call that array H. Then the children of H[i] are at H[i*2] and H[i*2+1]. The parent of H[i] is at H[i>>1]

In pseudocode, using 1-based indexes, we are given:

A[] = input array, N = input array size

We build H like this:

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]);

Note that we create the children before the parents so that the children are there when we need to get the maximum of their values.

Now, the query function:

//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*node; //left child of the node
       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);

This satisfies the requirement for queries in O(log N) time. It would be nice (and not too difficult) to exit early when you know there won't be an answer less than maxindex, but that would make the pseudocode a little less clear, so I'll leave it as an exercise


O(logN) seams to be impossible. You need at least to read the input until the first element which is greater (this might be the last one or none at all). So in the worst case you need to read the whole input which means O(N).

Improvements are only possible if there is some extra structure of your input like sorted than you could improve the algorithm to O(logN).

If there are multible queries you still need O(logN). You can check for multible queries at once and also cache the results for the case that the same queries comes again.

Tags:

Algorithm