I need to find how many elements are lower than a given one in a std::set
.
I thought the right function to use was std::lower_bound
which returns an iterator to the first element which is greater or equal to the given one....so the index of this iterator is what I'm looking for...but I can't find the index from the iterator:
#include <iostream>
#include <algorithm>
#include <set>
int main()
{
std::set<int> mySet;
mySet.insert( 1 );
mySet.insert( 2 );
mySet.insert( 3 );
mySet.insert( 4 );
std::set<int>::const_iterator found = std::lower_bound( mySet.begin(), mySet.end(), 2 );
if ( found != mySet.end() )
std::cout << "Value 2 was found at position " << ( found - mySet.begin() ) << std::endl;
else
std::cout << "Value 2 was not found" << std::endl;
}
This does not compile:
16:63: error: no match for 'operator-' (operand types are 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}' and 'std::set<int>::iterator {aka std::_Rb_tree_const_iterator<int>}')
16:63: note: candidates are:
In file included from /usr/include/c++/4.9/vector:65:0,
from /usr/include/c++/4.9/bits/random.h:34,
from /usr/include/c++/4.9/random:49,
from /usr/include/c++/4.9/bits/stl_algo.h:66,
from /usr/include/c++/4.9/algorithm:62,
from 3:
Using std::vector instead of std::set works perfectly.
Looks like operator- is not valid for a std::set::iterator
. Why?
Then, how can you easily (without calling std::previous
or std::next
untill bound is reached...this would not be efficient) find the position of a given iterator in the container? If you can't, then what alterantive can I use to find the index of a given element...?
The correct way to do a lower bound search is with std::set
's own lower_bound
function, which is specially designed to work with this sorted, associative, non-random-access container.
So, instead of this:
std::lower_bound( mySet.begin(), mySet.end(), 2 );
use this:
mySet.lower_bound(2);
This is logarithmic in the size of the container, which is much better than a std::count_if
approach (which doesn't know about the sortyness of the comparator, and therefore must visit all the nodes and is thus linear).
However, you then must also use std::distance
from the beginning to the lower bound, which is not only linear but also necessarily "slow" in practice (due to the non-random-access).
Nathan's solution seems optimal given that you do not want to simply find the lower bound, but find its distance from the "start" of the container.
Looks like operator- is not valid for a std::set::iterator. Why?
Indeed, an implementation of std::set::iterator::operator-()
can't exist in constant complexity since the elements are not contiguous in memory.
Then, how can you easily (without calling std::previous or std::next until bound is reached...this would not be efficient) find the position of a given iterator in the container?
You can't, std::set::iterator
is not a RandomAccessIterator. See std::distance()
documentation:
Complexity
Linear.
If you can't, then what alterantive can I use to find the index of a given element...?
I'd suggest to count your elements without having to compute an iterator distance: std::count_if()
can help us:
#include <iostream>
#include <algorithm>
#include <set>
int main()
{
std::set<int> mySet;
mySet.insert( 1 );
mySet.insert( 2 );
mySet.insert( 3 );
mySet.insert( 4 );
const std::size_t lower_than_three = std::count_if(
std::begin(mySet)
, std::end(mySet)
, [](int elem){ return elem < 3; } );
std::cout << lower_than_three << std::endl;
}
Demo
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