STL multimap associative containers - c++

Does anyone know how to create two multimap associative containers. The first one would have duplicate keys. Then i would like to post the algorithm to search for all duplicates and move them over to a second container and maybe delete the original duplicates in the first container.
i.e. :
typedef multimap< int, int, less< int > > mma;
mma contain1;
typedef multimap< int, less< int > > ne;
ne contain2;
cointain1.insert(mma::value_tpe(5, 2);
cointain1.insert(mma::value_tpe(5, 3);
cointain1.insert(mma::value_tpe(5, 3);
cointain1.insert(mma::value_tpe(6, 2);
any help would be much appreciated.

Read about multi_map::lower_bound and multi_map::upper_bound. They'll give you a pair of iterators that define a sequence of values that are equal to the argument. If the length of the sequence is greater than 1, you've got duplicates.

I'd recommend that you iterate over the first multimap, looking for duplicates. When you find them, you move them to the second multimap.
typedef multimap<int, int> mma;
mma contain1;
mma contain2;
contain1.insert(mma::value_type(5, 2);
contain1.insert(mma::value_type(5, 3);
contain1.insert(mma::value_type(5, 3);
contain1.insert(mma::value_type(6, 2);
int previous;
for (mma::iterator i = contain1.begin(); i != contain1.end(); )
if (i != contain1.begin() && i->first == previous)
{
contain2[i->first] = i->second;
// "maybe delete the original duplicates in the first container"...
contain1.erase(i++);
}
else
{
previous = i->first;
++i;
}

Related

Dijkstra shortest path algorith performance of std::priority_queue Vs std::set

I would like to understand the main difference of these containers regarding their time complexity.
I've tried 3 implementations of Dijkstra algorithm as described below:
1- with a simple array used as queue
2- with STL priority_queue
3- with STL set
the graph I've tested is quite big, it contains more than 150000 vertices, oriented and all the weight of the edges are positive.
the results I get are the following:
1 - with array the algorithm is pretty slow --> which is expected
2 - with STL priority_queue the algorithm run a lot faster than the array --> which is also expected
3 - with STL set the algorithm run incredibly fast, I'm talking about couple 100 times faster than the priority_queue --> I didn't expect to see this huge performance...
knowing that the std::priority_queue and std::set are data containers that store elements and both have basically the same insertion complexity O(log n), I don't understand this big performance difference between them. Have you any explanation about this?
thanks for your help,
Edited:
here it is an abstract of my implementations:
with std::set:
unsigned int Graphe::dijkstra(size_t p_source, size_t p_destination) const {
....
set<pair<int, size_t>> set_vertices;
vector<unsigned int> distance(listAdj.size(),
numeric_limits<unsigned int>::max());
vector < size_t
> predecessor(listAdj.size(),
numeric_limits < size_t > ::max());
distance[p_source] = 0;
set_vertices.insert( { 0, p_source });
while (!set_vertices.empty()) {
unsigned int u = set_vertices.begin()->second;
if (u == p_destination) {
break;
}
set_vertices.erase( { distance[u],
u });
for (auto itr = listAdj[u].begin();
itr != listAdj[u].end(); ++itr) {
int v = itr->destination;
int weigth = itr->weigth;
if (distance[v]
> distance[u] + weigth) {
if (distance[v]
!= numeric_limits<unsigned int>::max()) {
set_vertices.erase(
set_vertices.find(
make_pair(distance[v],
v)));
}
distance[v] = distance[u] + weigth;
set_vertices.insert( { distance[v],
v });
predecessor[v] = u;
}
}
}
....
return distance[p_destination];}
and with priority_queue:
unsigned int Graphe::dijkstra(size_t p_source, size_t p_destination) const {
...
typedef pair<size_t, int> newpair;
priority_queue<newpair, vector<newpair>, greater<newpair> > PQ;
vector<unsigned int> distance(listAdj.size(),
numeric_limits<unsigned int>::max());
vector < size_t
> predecessor(listAdj.size(),
numeric_limits < size_t > ::max());
distance[p_source] = 0;
PQ.push(make_pair(p_source, 0));
while (!PQ.empty()) {
unsigned int u = PQ.top().first;
if (u == p_destination) {
break;
}
PQ.pop();
for (auto itr = listAdj[u].begin();
itr != listAdj[u].end(); ++itr) {
int v = itr->destination;
int weigth = itr->weigth;
if (distance[v]
> distance[u] + weigth) {
distance[v] = distance[u] + weigth;
PQ.push(
make_pair(v, distance[v]));
predecessor[v] = u;
}
}
}
...
return distance[p_destination];}
SKIP
You are doubling up on the work really badly with the priority queue.
You are double inserting into the queue, because you can't modify or delete. That's normal and necessary, because you can't.
but then when those old values come out of the queue you need to "skip that iteration of the while loop".
Something like:
if (PQ.top().second != distance[PQ.top().first]) continue; // It's stale! SKIP!!
The underlying data-structure of std::priority_queue is a max-heap and for std::set its self-balanced binary search - basically a red-black tree for C++. So both of them ensures O(logn) time complexity in insertion, deletion and update operation.
But, as I mentioned the balanced binary search tree of std::set is being balanced automatically to keep its height logarithmic of number of nodes which ensures logarithmic query complexity irrespective of insertion order or after any operations. std::priority_queue is not self-balanced and can be very flat depending on insertion order. Although the self-balancing has it's own cost and so as of heapify after removing top, I think that's the reason for the performance gain.
Hope it helps!

Select random element in an unordered_map

I define an unordered_map like this:
std::unordered_map<std::string, Edge> edges;
Is there a efficient way to choose a random Edge from the unordered_map edges ?
Pre-C++11 solution:
std::tr1::unordered_map<std::string, Edge> edges;
std::tr1::unordered_map<std::string, Edge>::iterator random_it = edges.begin();
std::advance(random_it, rand_between(0, edges.size()));
C++11 onward solution:
std::unordered_map<std::string, Edge> edges;
auto random_it = std::next(std::begin(edges), rand_between(0, edges.size()));
The function that selects a valid random number is up to your choice, but be sure it returns a number in range [0 ; edges.size() - 1] when edges is not empty.
The std::next function simply wraps the std::advance function in a way that permits direct assignation.
Is there a efficient way to choose a random Edge from the unordered_map edges ?
If by efficient you mean O(1), then no, it is not possible.
Since the iterators returned by unordered_map::begin / end are ForwardIterators, the approaches that simply use std::advance are O(n) in the number of elements.
If your specific use allows it, you can trade some randomness for efficiency:
You can select a random bucket (that can be accessed in O(1)), and then a random element inside that bucket.
int bucket, bucket_size;
do
{
bucket = rnd(edges.bucket_count());
}
while ( (bucket_size = edges.bucket_size(bucket)) == 0 );
auto element = std::next(edges.begin(bucket), rnd(bucket_size));
Where rnd(n) returns a random number in the [0,n) range.
In practice if you have a decent hash most of the buckets will contain exactly one element, otherwise this function will slightly privilege the elements that are alone in their buckets.
Strict O(1) solution without buckets:
Keep a vector of keys, when you need to get a random element from your map, select a random key from the vector and return corresponding value from the map - takes constant time
If you insert a key-value pair into your map, check if such key is already present, and if it's not the case, add that key to your key vector - takes constant time
If you want to remove an element from the map after it was selected, swap the key you selected with the back() element of your key vector and call pop_back(), after that erase the element from the map and return the value - takes constant time
However, there is a limitation: if you want to delete elements from the map aside from random picking, you need to fix your key vector, this takes O(n) with naive approach. But still there is a way to get O(1) performance: keep a map that tells you where the key is in the key vector and update it with swap :)
This is how you can get random element from a map:
std::unordered_map<std::string, Edge> edges;
iterator item = edges.begin();
int random_index = rand() % edges.size();
std::advance(item, random_index);
Or take a look at this answer, which provides the following solution:
std::unordered_map<std::string, Edge> edges;
iterator item = edges.begin();
std::advance( item, random_0_to_n(edges.size()) );
The solution of
std::unordered_map<std::string, Edge> edges;
auto random_it = std::next(std::begin(edges), rand_between(0, edges.size()));
is extremely slow....
A much faster solution will be:
when assigning edges, simutaneously emplaces its keys to std::vector<std::string> vec
random an int index ranging from 0 to vec.size() - 1
then get edges[vec[index]]
you can see this problem:
problem 380. Insert Delete GetRandom O(1)
you can build a vector to use vector random iterators, get random values more efficiently. Like this:
class RandomizedSet {
public:
unordered_map<int, int> m;
vector<int> data;
RandomizedSet() {
}
bool insert(int val) {
if(m.count(val)){
return false;
} else{
int index = data.size();
data.push_back(val);
m[val] = index;
return true;
}
}
bool remove(int val) {
if(m.count(val)){
int curr_index = m[val];
int max_index = data.size()-1;
m[data[max_index]] = curr_index;
swap(data[curr_index], data[max_index]);
data.pop_back();
m.erase(val);
return true;
} else{
return false;
}
}
int getRandom() {
return data[rand() % data.size()];
}
};
/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet* obj = new RandomizedSet();
* bool param_1 = obj->insert(val);
* bool param_2 = obj->remove(val);
* int param_3 = obj->getRandom();
*/

C++ Intersect two maps on keys, keep value of first map

I'm experiencing a problem with C++ and maps and intersection.
Have 3 maps, the first two being map<int, double>, and the last one being map<int, CustomType>.
I want to remove all instances of map keys from the first 2 maps, that do not exist as a key in the 3rd map. In brief, I have the third map that contains a list of objects, and the first two maps that contain some data about the objects. At some point in time the map with the objects is cleaned up and some items removed (user interaction) and now want to clean up the other two maps respectively.
I've tried the following:
map<int, double> map1, map2;
map<int, CustomType> map3;
for (auto it = map1.cbegin(); it != map1.cend(); )
{
if ( map3.find(it->first) == map3.end() )
{
map2.erase(it);
map1.erase(it++);
}
else ++it;
}
This gives me an error "pointer being freed was not allocated" on the map1.erase line. I've looked into set_intersection but I don't believe it would work in this case since the values will be different.
Any assistance is appreciated.
You need to iterate map1 and map2 independently. You cannot use an iterator of map1 to manipulate another map (to erase from map2 or to perform any other operations with map2).
So the code should be something like this:
map<int, double> map1, map2;
map<int, CustomType> map3;
for (auto it = map1.cbegin(); it != map1.cend(); )
{
if ( map3.find(it->first) == map3.end() )
it = map1.erase(it);
else
++it;
}
for (auto it = map2.cbegin(); it != map2.cend(); )
{
if ( map3.find(it->first) == map3.end() )
it = map2.erase(it);
else
++it;
}
You are trying to erase an element from map2 with an iterator from map1. That won't work. You need to get the key value from the iterator and use that to erase from map2. And when you called erase for map1 you invalidated your iterator, because you removed the element it was pointing to. Increment the iterator, then use the key value to call map1.erase().
You was close to the solution, but the problem is that an iterator is substantially a pointer.
So you can use "it" to remove both on map1 and map2.
void removeUnexist(const map<int, double>& m, const map<int, CustomType>::iterator& it) {
i = m.find(it->first);
if(i == m.end()) {
m.erase(i);
}
}
map<int, double> map1, map2;
map<int, CustomType> map3;
for (auto it = map3.cbegin(); it != map3.cend(); it++) {
removeUnexist(map1, it);
removeUnexist(map2, it);
}

Is there a .at() equivalent for a multimap?

Is there any way to get an iterator to a multimap, for specific keys? For example:
multimap<string,int> tmp;
tmp.insert(pair<string,int>("Yes", 1));
tmp.insert(pair<string,int>("Yes", 3));
tmp.insert(pair<string,int>("No", 5));
tmp.insert(pair<string,int>("Maybe", 1));
tmp.insert(pair<string,int>("Yes", 2));
multimap<string,int>::iterator it = tmp.at("Yes);
Then I could use it for the work I want to do. Is this possible in C++? Or do we have to just cycle through the multimap, element by element, and check for the key before doing the work?
You have find for a single key value pair (any matching the key), or equal_range to get all of the pairs that match a given key (this seems to be your best bet.)
multimap<Key, T> only sort elements by its Key, so we can only find all the elements whose key value equals "Yes", then check each element one by one.
typedef multimap<string,int>::iterator Iterator;
pair<Iterator, Iterator> iter_range = tmp.equal_range("Yes");
Iterator it;
for (it = iter_range.first; it != iter_range.second; ++it) {
if (it->second == 3) {
break;
}
}
if (it != tmp.end()) {
tmp.erase(it);
}
In fact it's better to use multiset<T> in this case:
multiset< pair<string, int> > temp;
temp.insert(make_pair("Yes", 1));
temp.insert(make_pair("Yes", 3));
multiset< pair<string, int> >::iterator iter = temp.find(make_pair("Yes", 1));
if (iter != temp.end()) {
temp.erase(iter); // it erase at most one element
}
temp.erase(make_pair("Yes", 3)); // it deletes all the elements that equal to make_pair("Yes", 3)

STL Multimap Remove/Erase Values

I have STL Multimap, I want to remove entries from the map which has specific value , I do not want to remove entire key, as that key may be mapping to other values which are required.
any help please.
If I understand correctly these values can appear under any key. If that is the case you'll have to iterate over your multimap and erase specific values.
typedef std::multimap<std::string, int> Multimap;
Multimap data;
for (Multimap::iterator iter = data.begin(); iter != data.end();)
{
// you have to do this because iterators are invalidated
Multimap::iterator erase_iter = iter++;
// removes all even values
if (erase_iter->second % 2 == 0)
data.erase(erase_iter);
}
Since C++11, std::multimap::erase returns an iterator following the last removed element.
So you can rewrite Nikola's answer slightly more cleanly without needing to introduce the local erase_iter variable:
typedef std::multimap<std::string, int> Multimap;
Multimap data;
for (Multimap::iterator iter = data.begin(); iter != data.end();)
{
// removes all even values
if (iter->second % 2 == 0)
iter = data.erase(iter);
else
++iter;
}
(See also answer to this question)