I'm trying to figure out how std::multimap
iterators work, therefore I've created a simple example that shows the substance of my problem. If uncomment case 1, I expect iterator to point to the first element with the key 1, but in reality it prints all the values associated with key 0 (like nothing was erased) and sometimes it crashes, probably because iterator is invalid. However if uncomment case 2, all the values with key 1 are properly deleted.
Is there any way to know what is the next valid iterator for the multimap
after erasure?
(for example std::vector.erase(...)
returns one)
std::multimap<int, int> m;
for(int j=0; j<3; ++j) {
for(int i=0; i<5; ++i) {
m.insert(std::make_pair(j, i));
}
}
for(std::multimap<int, int>::iterator it=m.begin(); it!=m.end();) {
printf("%d %d\n", (*it).first, (*it).second);
++it;
if( (*it).second == 3 ) {
//m.erase(0); //case 1
m.erase(1); //case 2
}
}
When you call m.erase(0)
in you example, it
points at an element with the key 0
- so it
is invalidated. m.erase(1)
works, because when it is called the first time, it
is not pointing to an element with the key 1
, so it is not affected. In later iterations, no elements with the key 1
remain, so nothing is deleted, and no iterator is affected.
multimap
does not have an erase
-method that returns the next valid iterator. One alternative is to call it = m.upper_bound(deleted_key);
after the deletion. This is logarithmic, though, which might be too slow for your scenario (erase(x)
and upper_bound
would be two logarithmic operations).
Assuming you want to erase the key your iterator is currently pointing to, you could do something like this (otherwise, erase
is fine, of course; not tested):
std::multimap<int, int>::iterator interval_start = m.begin();
for(std::multimap<int, int>::iterator it=m.begin(); it!=m.end(); ++it) {
if(interval_start->first < it->first) // new interval starts here
interval_start == it;
if( (*it).second == 3 ) {
std::multimap<int, int>::iterator interval_end = it;
while((interval_end != m.end()) && (interval_end->first == it->first)) {
++interval_end; // search for end of interval - O(n)
}
m.erase(interval_start, interval_end); // erase interval - amortized O(1)
it = interval_end; // set it to first iterator that was not erased
interval_start = interval_end; // remember start of new interval
}
}
This uses one linear operation, all the rest are constant time. If your map is very large, and you only have few items with equal keys, this will likely be faster. However, if you have many items with equal keys, the search for the end of the interval, is probably better done using upper_bound
(O(log n)
instead of O(n)
when searching the end of the interval).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With