tags:

views:

460

answers:

3

I've setup a std map to map some numbers, at this point I know what numbers I'm mapping from an to, eg:

std::map<int, int> myMap;

map[1] = 2;
map[2] = 4;
map[3] = 6;

Later however, I want to map some numbers to the lowest number possilbe that is not in the map, eg:

map[4] = getLowestFreeNumberToMapTo(map); // I'd like this to return 1
map[5] = getLowestFreeNumberToMapTo(map); // I'd like this to return 3

Any easy way of doing this?

I considered building an ordered list of numbers as I added them to the map so I could just look for 1, not find it, use it, add it etc.

A: 

I'd use a bidirectional map class for this problem. That way you can simply check if value 1 exists etc.

Edit

The benefits of using a bimap is that there already exist robust implementations of it and even if searching for the next free number is O(n) it is only an issue if n is large (or possibly if n is moderate and this is called very frequently). Overall making for a simple implementation that is unlikely to be error prone and easily maintainable.

If n is large or this operation is performed very frequently than investing the effort of implementing a more advanced solution is merited. IMHO.

Kris
this hides the algorithm... which is the Q. Which algo is used to search the keyed items on that class?
jpinto3912
Checking if value n exists to find lowest available sounds like an O(n) exercise
John Pirie
+5  A: 

Something like

typedef std::set<int> SetType;
SetType used;           // The already used numbers
int freeCounter = 1;    // The first available free number

void AddToMap(int i)
{
    used.insert(i);
    // Actually add the value to map
}

void GetNewNumber()
{
    SetType::iterator iter = used.lower_bound(freeCounter);
    while (iter != used.end() && *iter == freeCounter)
    {
        ++iter;
        ++freeCounter;
    }
    return freeCounter++;
}

If your map is quite big but sparse, this will work like o(log(N)), where N is the number of items in the map - in most cases you won't have to iterate through the set, or just make a few steps. Otherwise, if there are few gaps in the map, then you would better have a set of free items in the range [1..maxValueInTheMap].

Dmitry Risenberg
This is a better algorithm than it seems at first sight. Note that although *some* calls to GetNewNumber() might take many steps, the *total* number of times freeCounter is incremented (and hence the total running time of GetNewNumber(), divided by the logarithmic factor for set operations) is bounded by the number of items assigned in the map (since if you've assigned N numbers, freeCounter ≤ N+1). So if you've assigned up to map[N], all the calls to GetNewNumber() so far will have taken only O(N log N) time: GetNewNumber is O(log N) amortised. http://en.wikipedia.org/wiki/Amortized_analysis
ShreevatsaR
(In other words, the algorithm works well even when the map is not sparse; it is always O(log N) amortised.)
ShreevatsaR
+1  A: 
ephemient
As I pointed out in the comment to the other answer, the other solution does not actually require the used numbers to be sparse (to be good upto log factors, at least.) This answer is interesting: the question seems to only have inserts, no deletes, and potentially arbitrarily high numbers assigned, but yours is with both inserts and deletes, and the total "range" being small enough that it is possible to keep a list of unused numbers.
ShreevatsaR
I mention "sparse" because if you only have 5 unused numbers at a time, O(5) is nice and small, but if half of them are unused, O(log n) is (relatively) a lot bigger.
ephemient