I am aware of the .find() method to check whether or not an element exists in a map. But suppose i have a map -- map -- with about 2000 elements and I want to add an element to the list. I first have to check if the element exists. Isn't it a bit inefficient to use .find() because the iterator has to 'iterate' over element in the map? Is there a more efficient and less time consuming way to check whether or not an element exists in a map?
std::map has logarithmic look-up complexity, so you won't have to visit all the elements to determine whether an element is in the map.
On the other hand, you can make the look-up and insert operation more concise by using std::map::emplace or std::map::insert:
auto [iter, ok] = the_map.emplace(the_key, a_value);
if (ok) {
// element was not in map so got inserted
}
This assumes that a_value either already exists, or that it is not an issue to construct it even if it does not end up being inserted into the map.
If you really need to use 'find and insert' idiom (and that could be because you can't construct a value object for the same key twice under any circumstances) and you are concerned about twice logarithmic complexity, you'd have to use std::map::equal_range.
The way to do this would be first use equal_range with your key, and than use one of returned iterators as a hint to insert.
Related
I find myself in a situation where I have to access a std::map by position. Since std::advance(iter, position) is slow af, I want to add a second data structure to speedup this operation.
My idea: Maintain a vector for every key-value pair in the map. Then access the vector by position, vector[position]->second.
When erasing/inserting a new element I obviously have to remove the iterator from the vector. But besides that the iterator-preserving properties of std::map seem to be sufficient.
Question: Is this a good idea?
Alternative: Maintain a vector of map::keys. Then access vector by position an use the key to lookup the value in the map,map[vector[position]]. Is this smarter?
If iteration through the map is your primary performance issue, then you should be using a flat_map (not as of yet part of the standard, but Boost has a decent one). If you don't have one of those, just use a vector<pair> that you keep sorted using the same comparison function you would have used in your map.
You can use std::lower_bound as the equivalent function for being able to find a value by its key. You can also use the iterator returned from std::lower_bound as the position for doing a single-element insertion of a new element. Everything else works just like any other vector; simply keep it sorted and you'll be fine.
std::map search, removal, and insertion operations have logarithmic complexity. The same complexity can be achieved by a sorted std::vector
Don't use a map, but a vector. Keep it sorted by key. Binary search by key is logaritgmic. Access by position is the fastest.
The only drawback is that inserting and removing needs memory reallocation. You may test its performance and consider if it's worth.
I've been wondering if it's possible to iterate through part of a map, based on a starting key that may or may not exist. If I had a map with keys full of positions, I would like to say,
"return an iterator such that if _Key doesn't exist, I am returned the closest iterator before _Key"
I could use this to emplace_hint a new value, or loop through a specific range of positions even if the search key does not exist. Is this kind of functionality easily available to us in some way?
Use lower_bound and then decrease iterator, after checking it doesn't point to begin iterator, as Andre Kostur said.
It returns an iterator pointing to the first element in the container whose key is not considered to go before k (i.e., either it is equivalent or goes after). Complexity O(log n).
This question clarified that iterators remain valid after insertion. I'd like to go a bit further and ask for verification that this is expected behavior:
std::map automatically sorts based on key.
insertion into the map thus automatically places the new element in a sorted location somewhere inside the map
An iterator++ operation therefore could return a different element after insertion than it would have prior to insertion.
Does this all make sense?
this's true if the new inserted element is after the iterator you are visiting.
I use a std::set to sort a vector of unordered duplicate values. Every time I find an element in my set, I need to know the position (index) of the element as well. There are a lot of elements (hundreds of thousands) in my set, and using std::distance() gives me abysmal performance.
Is std::distance the only way to go?
You can sort the elements in place using the std::sort() algorithm. Then when you find an element in the vector using binary_search() just subtract the result of a call to begin() from the iterator pointing to the element.
Another alternative is to use std::partial_sort_copy() if you don't want to overwrite your original vector. Just sort into another vector and you can do the same thing I described above.
Do you know if it is any difference in performance when I access a std::map element using find or operator []?
One returns an iterator and the other a const ref to the object.
Which one might be quicker becuase of all of the behind the scene of the STL?
When you use [] on a key that doesn't exist, the default element will be inserted. This default element depends on your map definition (for example, for an int it will be a zero).
When you use find, there is no "automatic" insertion, so it can be quite faster if you often search for keys that does not exist.
find() is O(n). operator [] is O(1). Therefore the latter is (usually) faster.