Store C++ maps by value - c++

Is it possible to store pairs such as ordered by value in a map. Therefore finding the min and max element would be very cheap for me. I know in gnu libstdc++, map is implemented by using a RB-tree, but couldn't figure out how to store elements by ordered by value. I don't want to exchange keys with values, because my values don't have to be unique.
Note: I know about max_element and min_element functions.

You could use a std::multiset, if you only need your values. Example:
#include <set>
#include <map>
int main(){
typedef std::map<int,double> your_map_type;
your_map_type your_map;
// populate your_map
std::multiset<double> your_values;
for(your_map_type::const_iterator it = your_map.begin(); it != your_map.end(); ++it){
your_values.insert(it->second);
}
}

Related

C++: 'unique vector' data structure

I need a data structure like std::vector or std::list whose elements will be unique. In most of time I will call push_back on it, sometimes maybe erase. When I insert an element which is already there, I need to be notified either by some boolean or exception.
And the most important property it should have: the order of insertions. Each time I iterate over it, it should return elements in the order they were inserted.
We can think other way: a queue which guarantees the uniqueness of elements. But I don't want to pop elements, instead I want to iterate over them just like we do for vector or list.
What is the best data structure for my needs?
You can use a std::set
It will return a pair pair<iterator,bool> when the insert method is called. The bool in the pair is false when the element already exists in the set (the element won't be added in that case).
Use a struct with a regular std::vector and a std::set.
When you push, check the set for existence of the element. When you need to iterate, iterate over the vector. If you need to erase from the vector, also erase from the set.
Basically, use the set as an aside, only for fast "presence of an element" check.
// either make your class a template or use a fixed type of element
class unique_vector
{
public:
// implement the various operator you need like operator[]
// alternatively, consider inheriting from std::vector
private:
std::set<T> m_set; // fast lookup for existence of elements
std::vector<T> m_vector; // vector of elements
};
I would prefer using std::unordered_set to stores existing elements in a std::vector and it has faster lookup time of O(1), while the lookup time of std::set is O(logn).
You can use Boost.MultiIndex for this:
Live On Coliru
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
using namespace boost::multi_index;
template<typename T>
using unique_list=multi_index_container<
T,
indexed_by<
sequenced<>,
hashed_unique<identity<T>>
>
>;
#include <iostream>
int main()
{
unique_list<int> l;
auto print=[&](){
const char* comma="";
for(const auto& x:l){
std::cout<<comma<<x;
comma=",";
}
std::cout<<"\n";
};
l.push_back(0);
l.push_back(1);
l.push_back(2);
l.push_back(0);
l.push_back(2);
l.push_back(4);
print();
}
Output
0,1,2,4

Efficiently find number of elements in range in C++ set [duplicate]

I want to find rank of an element in stl set. I am able to traverse from beginning to that element and find out its rank but that is taking O(n). Is there any method to find the rank in O(logn).
No; a balanced tree does not need to store the number of descendants of each node, which is required to more quickly compute distance( s.begin(), iter ) for std::set s and iterator iter (which is what I suppose you mean). Therefore the information simply doesn't exist except by counting the items one by one.
If you need to perform many such computations, copy the set into a sorted, random-access sequence such as vector or deque, but then modification of the sequence becomes expensive.
A tree data structure that does what you ask probably exists in a free library somewhere, but I don't know of one.
What you are looking for is called an Order Statistic Tree. If you are using GNU C++ library, you should have an extension available for building order statistic trees. A short example is given below:
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <cstdio>
using namespace std;
using namespace pb_ds;
typedef tree<
int, /* key type */
null_mapped_type, /* value type */
less<int>, /* comparison */
rb_tree_tag, /* for having an rb tree */
tree_order_statistics_node_update> order_set;
int main()
{
order_set s;
s.insert(10);
s.insert(20);
s.insert(50);
s.insert(25);
printf("rank of 25 = %d\n", s.order_of_key(25));
}
Output should be rank of 25 = 2. For more examples, you can see this file.
The functionality of a sorted vector suggested by #Potatoswatter is provided by the flat_set from Boost.Container. The documentation lists the following trade-offs
Faster lookup than standard associative containers
Much faster iteration than standard associative containers
Less memory consumption for small objects (and for big objects if shrink_to_fit is used)
Improved cache performance (data is stored in contiguous memory)
Non-stable iterators (iterators are invalidated when inserting and erasing elements)
Non-copyable and non-movable values types can't be stored
Weaker exception safety than standard associative containers (copy/move constructors can throw when shifting values in erasures and insertions)
Slower insertion and erasure than standard associative containers (specially for non-movable types)
There is actually a built-in solution if you are using GCC, but Subhasis Das's answer is somewhat outdated and will not work with newer versions of GCC due to updates. The header is now
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
and the set structure is
typedef tree<
int,
null_type,
std::less<int>,
rb_tree_tag,
tree_order_statistics_node_update> ordered_set;
Alternatively, if a multiset is needed, the std::less<int> can be repalced with std::less_equal<int>.
Here's a demonstration of find-by-rank:
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
#include <iostream>
typedef tree<int, null_type, std::less_equal<int>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
int main()
{
ordered_set s;
s.insert(10);
s.insert(20);
s.insert(50);
s.insert(25);
for(int i=24; i<=26; i++) std::cout << "Rank of " << i << ": " << s.order_of_key(i) << std::endl;
}
I think there is a lower_bound function in the STL set in C++ and this can be used to find the rank of an element in a set. Take a look at https://www.geeksforgeeks.org/set-lower_bound-function-in-c-stl/.

Comparing unordered_map vs unordered_set

First of all, what is the main difference between them?
The only thing i've found is that unordered_set has no operator [].
How should i access an element in unordered_set, since there is no []?
Which container is using random access to memory(or both)?
And which one of them faster in any sense or using less memory?
They are nearly identical. unordered_set only contains keys, and no values. There is no mapping from a key to a value, so no need for an operator[]. unordered_map maps a key to a value.
You can use the various find methods within unordered_set to locate things.
you can use iterators to access elements.
unordered_set <string> u{
"Dog",
"Cat",
"Rat",
"Parrot",
"bee"
};
for(auto& s:u){
cout << s << ' ';
}
unordered_set<string>::const_iterator point = u.find("bee");
How should I access an element in unordered_set (C++17)?
In C++ 17 a new function extract is added to unordered_set.
Specially, this is the only way to take move only object out of the set.
https://en.cppreference.com/w/cpp/container/unordered_set/extract
For example if you want third element of your unordered set.
Advance the iterator
std::advance(it,2);
Then extarct the value
s.extract(it).value();
Here is the complete code. try on any C++17 compiler.
#include <iostream>
#include <string>
#include <unordered_set>
#include <iterator>
int main()
{
//CREATE AN OBJECT
std::unordered_set<std::string> s;
//INSERT DATA
s.insert("aee");
s.insert("bee");
s.insert("cee");
s.insert("dee");
//NEED TO INCLUDE "iterator" HEADER TO USE "std::advance"
auto it = s.begin();
std::advance(it,2);
//USING EXTRACT
std::string sval = s.extract(it).value();
std::cout<<sval;
}
Note: if queried for out of bound index, nothing happens. No result.
Try changing your code
//ONLY FOUR ELEMENTS
std::advance(it,8);
//USING EXTRACT
std::string sval = s.extract(it).value();

Algorithms for traversing a collection in sorted order without modifying the collection?

Let us say we have a collection like the following: {12, 10, 4, 5, 7}
I'd like to preserve the order of the collection so the indices will remain consistent but traverse the collection in a sorted order like so {12, 10, 7, 5, 4}.
What I've thought of is to make another collection of pointers to the elements and then sort the pointers.
What are your thoughts? Has an algorithm like this already been implemented in C++?
Edit: In my case, I have a vector<vector<double>> and I would like to traverse the outer-vector collection in non-increasing order based on the sum of the inner-vectors.
If you want to maintain both orders as an ongoing thing, while elements are added and removed, then you could use boost multi-index:
http://live.boost.org/doc/libs/1_34_0/libs/multi_index/doc/tutorial/basics.html#multiple_sort
This example from that page is pretty much what you want, except sorted in the opposite order:
multi_index_container<
int,
indexed_by<
sequenced<>, // sequenced type
ordered_unique<identity<int> > // another index
>
> s;
If you just want it as a one-off operation, your idea of sorting the pointers sounds fine. As a slight variant, you could create a vector of std::pair<int,size_t>, each pair consisting of a value followed by its index. Then sort the vector of pairs with std::sort and specify std::greater<std::pair<int,size_t> > as the comparator.
Edit:
Given your actual case, I would definitely advise sorting pairs, because that way you only have to compute the sum of each inner-vector once, and store it in the pair. If you were sorting just pointers/indexes, you'd have to calculate two sums for each comparison. so:
typedef vector<vector<double> >::const_iterator It;
typedef pair<double, It> Pair;
vector<Pair> pairvec;
pairvec.reserve(input.size());
for (It it = input.begin(); it != input.end(); ++it) {
// some people would split this over multiple lines
pairvec.push_back(make_pair(accumulate(it->begin(), it->end(), 0.0), it));
}
sort(pairvec.begin(), pairvec.end(), greater<Pair>());
Then you can iterate over pairvec. Make It a non-const iterator if you need to.
One of the ways to do it (for a linked list) is to mentain two levels of pointers: one for original order, and the second for descending, like in
typedef struct node{
int key;
struct node* next;
struct node* next_descending;
};
You could store the index values in an array, then write/adapt a sorting algorithm that sorts the index values based on what they index in the original array.
Not sure what your runtime/memory constraints are, but the most straightforward and practical solution IMHO is to simply insert all items from the std::vector into a std::multiset and just traverse the multiset from beginning to end. The overall runtime would be O(n*log(n)) and would require O(n) extra memory for the multiset.
#include <set>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> data;
data.push_back(12);
data.push_back(10);
data.push_back(4);
data.push_back(5);
data.push_back(7);
multiset<int> sorted(data.begin(), data.end());
for (multiset<int>::iterator it=sorted.begin();it!=sorted.end();it++)
cout<<*it<<" ";
cout<<endl;
for(vector<int>::iterator it=data.begin();it!=data.end();it++)
cout<<*it<<" ";
cout<<endl;
return 0;
}

Make Map Key Sorted According To Insert Sequence

Without help from additional container (like vector), is it possible that I can make map's key sorted same sequence as insertion sequence?
#include <map>
#include <iostream>
using namespace std;
int main()
{
map<const char*, int> m;
m["c"] = 2;
m["b"] = 2;
m["a"] = 2;
m["d"] = 2;
for (map<const char*, int>::iterator begin = m.begin(); begin != m.end(); begin++) {
// How can I get the loop sequence same as my insert sequence.
// c, b, a, d
std::cout << begin->first << std::endl;
}
getchar();
}
No. A std::map is a sorted container; the insertion order is not maintained. There are a number of solutions using a second container to maintain insertion order in response to another, related question.
That said, you should use std::string as your key. Using a const char* as a map key is A Bad Idea: it makes it near impossible to access or search for an element by its key because only the pointers will be compared, not the strings themselves.
No. std::map<Key, Data, Compare, Alloc> is sorted according to the third template parameter Compare, which defaults to std::less<Key>. If you want insert sequence you can use std::list<std::pair<Key, Data> >.
Edit:
As was pointed out, any sequential STL container would do: vector, deque, list, or in this particular case event string. You would have to decide on the merits of each.
Consider using a boost::multi_index container instead of a std::map. You can put both an ordered map index and an unordered sequential index on your container.