2

You are given an array of N positive integers, A1,A2,…,An. You have to answer Q queries. Each query consists of two integers L and K. For each query I have to tell the Kth element which is larger than or equal to L in the array when all such elements are listed according to increasing order of their indices.

Example A = 22,44,12,16,14,88,25,49

Query 1: L = 3 K = 4 Since all elements are greater than 3. So we list the whole array i.e. 22,44,12,16,14,88,25,49. 4th element among these elements is 16

Query 2: L = 19 K = 5 Listed elements 22,44,88,25,49. 5th element among these is 49.

What I have done: for each query iterate over the whole array and check the Kth element which is larger than or equal to L. Complexity : O(Q*N)

What I require: O(Q*logN) complexity.

Constraints : 1<=Q<=10^5 1<=N<=10^5 1<=Ai<=10^5

lassaendie
  • 105
  • 8

1 Answers1

2

One possible way to solve this task is to use immutable binary (RB) tree.

First you need to sort your array in ascending order, storing original indices of the elements next to the elements.

Traverse array in reverse (descending) order, adding elements one by one to immutable binary tree. Key in the tree is original index of the element. Tree is immutable, so by adding element I mean constructing new tree with added element. Save the tree created on each step near the corresponding element (the element that was last added to the tree).

Having these trees constructed for each element you can do your queries in O(log N) time.

Query: First, perform a binary search for L in sorted array (O(log N)) for the element that is bigger than L. You'll find the element and corresponding tree of indices of the elements that are bigger than L. In this tree you can find K-th largest index in O(log N) time.

The whole algorithm will take O(N log N + Q log N) time. I don't believe that it is possible to do better (as sorting the original array seems to be inevitable).


The key of this approach is using immutable binary tree. This structure shares the properties of mutable binary tree, such as insertion and search in O(log N), while staying immutable. When you add element to such tree, the previous version of the tree is preserved, only nodes that are different from the previous "version" of the tree are recreated. Usually it's O(log N) nodes. Thus creating N trees from elements of your array will require O(N log N) time and O(N log N) space.

You can use immutable RB tree implementation in Scala as the reference.

Aivean
  • 10,692
  • 25
  • 39
  • 1
    I have already followed this approach. I used map in C++ to store maps for all the elements. Maps are internally represented as BST's. How ever this approach is too memory intensive and is not what i require. In the worst case the memory used in this approach is O(N^2) and not O(N log N) as you suggest which is 10^10 and gives me a runtime error – lassaendie Sep 26 '15 at 08:04
  • @lassaendie, [RB tree](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) is balanced, it has guaranteed height of O(log N). As it's update requires O(log N) time in worst case it also means it requires O(log N) additional space (as you recreate only the changed path, sharing the rest). So, most probably you are just using the wrong data structures or using them in wrong way. – Aivean Sep 26 '15 at 08:27
  • 1
    The first tree will have 1 node . The second tree will have two nodes and so on till the last tree has N nodes. Total space = 1+2+3....N = N^2. Te additional space used at each update is 1 node. – lassaendie Sep 26 '15 at 08:39
  • 1
    I was thinking of a similar solution till I realized the space complexity is in fact N^2. Given that, time complexity can't be smaller as you'd have to fill this helper structure first and that itself is N^2. – Amit Sep 26 '15 at 08:42
  • @lassaendie, the first tree will have 1 node, second will have 2 nodes (1 node will be created + 1 node reused from the first tree, so additional memory consumption will be 1 node). Third tree, depending on the elements will consume space for 1-2 nodes. Each other tree will consume O(log N) **additional** space, where N is the size of the previous tree. **Previous tree is reused almost completely.** – Aivean Sep 26 '15 at 08:59
  • How will the third tree consume less than 3 nodes when 3 indices are available which have values greater than or equal to the 3rd element – lassaendie Sep 26 '15 at 09:05
  • @lassaendie, I don't know how else I can explain it. There are data structures that are [immutable](http://stackoverflow.com/questions/3233473/immutable-data-structures-performance) (also they are called purely functional and [persistent](https://en.wikipedia.org/wiki/Persistent_data_structure)). Please read carefully these links. The point is, when you modify such structure, it's previous state is preserved and reused and thus total space consumed by previous and current state together is much less than the size of the previous state times two. – Aivean Sep 26 '15 at 09:14
  • 1
    @Aivean Let me have a look :) Thanks for the patience – lassaendie Sep 26 '15 at 09:16
  • 1
    @Aivean - looks like you're right. I didn't even pay attention to the use of immutable structures here and that's the key part. Shame on me for not paying attention :-) – Amit Sep 26 '15 at 10:18
  • 2
    If you can batch the queries, then you could sort them so as to answer them in descending order of L. Then (assuming that you also sort A) you can use a less exotic data structure, such as a skip list annotated with information about the number of elements <= the current point, to answer each query, putting more and more elements back in as L decreases. Immutable data structures look like cool technology, though. – mcdowella Sep 26 '15 at 10:19
  • @mcdowella, cool idea. Regular mutable RB tree will work in this case also. – Aivean Sep 26 '15 at 10:35
  • @mcdowella Thanks. I solved it using the approach you mentioned :) – lassaendie Sep 26 '15 at 13:01