7

I need to insert an element into sorted range, but I also need to know its index (number of elements in range that are less then the element). I want to do this in O(logN) time. Can I do this with basic C++ containers?

I was thinking to use std::multimap, with this container I can insert the element into its place with O(logN) complexity. But to get the index, I need to call std::distance, which takes O(N) operations, since multimap iterator is not random access.

Another way is to use sorted std::vector and std::binary_search algorithm. In this case the search takes O(logN), but the insertion will take O(N) operations, since the insertion to the middle of vector is linear operation.

So, is there std/boost container that I can use to reach the result, or I need to implement my own struct for this? Thanks!

  • I'm not up to speed with the complexity guarantees of Boost MultiIndex, but you could have a look – sehe Feb 25 '16 at 21:20
  • I guess it is possible with a custom tree / skiplist implementation, when keeping track of the number of elements represented by each (inner) node. But you're aware that such an index gets invalidated once you insert another item? – leemes Feb 25 '16 at 21:27
  • How big is the range of indexes we are talking about? If it is not that big (some millions) you could use a Fenwick tree, which is pretty easy to code. Otherwise you could code a balanced binary tree and remember the number of nodes in every subtree. AFAIK std containers are of no big help here. Would like to know, whether there is something in boost though. – ead Feb 26 '16 at 12:35
  • Possible duplicate of [C++ - Fastest way to add an item to a sorted array](https://stackoverflow.com/questions/28408240/c-fastest-way-to-add-an-item-to-a-sorted-array) – phuclv Oct 23 '17 at 09:52

2 Answers2

4

You can use Boost.MultiIndex's ranked indices:

Live Coliru Demo

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ranked_index.hpp>
#include <boost/multi_index/identity.hpp>

using namespace boost::multi_index;
using ranked_int_set=multi_index_container<
  int,
  indexed_by<
    ranked_unique<identity<int>>
  >
>;

#include <iostream>

int main()
{
  ranked_int_set s={0,2,4,6,8,10,12,14,16};

  auto it=s.insert(9).first;
  std::cout<<"9 was inserted at position #"<<s.rank(it)<<"\n";
  std::cout<<"14 is located at position #"<<s.find_rank(14)<<"\n";
}

Output

9 was inserted at position #5
14 is located at position #8
Joaquín M López Muñoz
  • 5,243
  • 1
  • 15
  • 20
2

No. I looked for it.

There is a way you can implement this. Start with a binary tree or skip-list, and maintain the size of the subtrees/skips (a bit of extra overhead -- when items are inserted, you have to backtrack to parents/skips and increment, and similar for deletes).

Then you can get indexes in lg n time, random access (by index or offset) in lg n time, and keep it sorted at the same time.

My attempts to find a pre-written container that did this was fruitless, and the project was canned so I did not get around to writing it.

A full-on database could be used, with the sorted column indexed, and you might be able to get the number less-than reasonably quickly.

Odds are if a simple linear sorted vector isn't reasonable (with its expensive insert-in-middle), you might want to consider a database anyhow.

As an example of a container that looks promising but failed, Boost's MultiIndex container lets you index a container in multiple ways, but the sequential and ordered indexes are independent. So you can tell what order you inserted the item, and where it goes before/after in sort, but not the index it has in the sort.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524