A first approach is to do a copy of array and sort it. Afterward you traverse the original array and on each item you perform a binary search for determing the rank. During this traversing you produce the desired sequence. With this aproach you takes O(n) for the copy, plus O(n lg n) for the sort and finally O(n lg n) for producing the sequence of ranks.
Another way is to insert all the items in a binary search tree (a balanced one such as an avl or red-black). This takes O(n lg n). Your binary tree must support "the rank extension"; that is, the sizes of each subtree must be stored in the nodes. These trees can export the operation position(key)
which returns the rank of key
.
Afterward, you traverse your array and for each entry you call to position(array[i])
. During this process you are producing the sequence of rank which is parallel to your array. This takes O(n lg n).
I think that the advantage of this approach respect to copy into an array of pairs and then sort it or simply to sort a copy of array and then to determine the rank by searching with binary search in the copied array, is that you avoid the extra copy from the sorted array of pairs to the sequence of ranks.
Added and corrected:
According to @xiaotian-peiI answer, I think it would be even better simply to insert pairs (key, index) in a deterministically balanced binary search tree (avl or red-black) sorted by keys; that takes O(n lg n). Then you traverse the binary tree inorder extracting the indexes, what takes O(n). Finally you free the tree, what takes O(n). So the total would be O(n lg n) + O(n) + O(n)
Maybe still more efficient according to scale and not the same complexity: to use a heap of pairs (key,index) and successively extract from it for building the sequence of ranks.
And very probably faster and sure less space consuming: the algorithm published by Jarod42, what I think is O(n) + O(n lg n) + O(n) too, but that would profit more the cache