计数元素低于std :: set中的给定值

时间:2016-03-09 14:22:33

标签: c++ stl set

我需要找到std::set中有多少元素低于给定元素。

我认为正确使用的函数是std::lower_bound,它将迭代器返回到第一个元素,该元素大于或等于给定的一个....所以这个迭代器的索引就是我所知道的寻找...但我无法从迭代器中找到索引:

#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;
}

这不编译:

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:

使用std :: vector而不是std :: set可以perfectly

看起来运营商对std::set::iterator无效。为什么? 然后,你怎么能轻松(没有调用std::previousstd::next直到达到...这不会有效)在容器中找到给定迭代器的位置?如果你不能,那么我可以用什么替代方法来找到给定元素的索引......?

5 个答案:

答案 0 :(得分:3)

  

看起来operator-对std :: set :: iterator无效。为什么?

实际上,std::set::iterator::operator-()的实现不能以复杂的常量存在,因为元素在内存中不是连续的。

  

然后,你怎么能轻松(没有调用std :: previous或std :: next直到达到绑定...这样效率不高)在容器中找到给定迭代器的位置?

你不能,std::set::iterator不是RandomAccessIterator。请参阅std::distance()文档:

  

<强>复杂性

     

线性。

  

如果你不能,那么我可以用什么替代方法来找到给定元素的索引......?

我建议您计算元素而不必计算迭代器距离:std::count_if()可以帮助我们:

#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

答案 1 :(得分:2)

由于std::set::iteratorBidirectionalIterator,除非我们使用减量运算符,否则我们无法从中减去std::set<int> mySet; // fill values int counter = 0; for (auto it = mySet.begin(), *it < some_value && it != mySet.end(); ++it) { if (e < some_value) counter++; } 。我们可以做的只是走集合并计算迭代次数,直到我们达到比我们想要的更大的数字。

mySet.size()

这是一个最差的{{1}}次迭代,它与处理BidirectionalIterator时的速度一样快。

另请注意,std::lower_bound没有O(log N)复杂性,因为我们没有使用RandomAccessIterator。使用非RandomAccessIterator时,它具有线性复杂性。

答案 2 :(得分:2)

进行下界搜索的正确方法是使用std::set's own lower_bound function,它是专门为与这种排序的,关联的,非随机访问的容器一起使用而设计的。

所以,代替这个:

std::lower_bound( mySet.begin(), mySet.end(), 2 );

使用此:

mySet.lower_bound(2);

这是容器大小的对数,比a std::count_if approach(它不知道比较器的种类,因此必须访问所有节点并且是线性的)好得多。 / p>

但是,然后,您还必须从头到下使用std::distance,这不仅是线性的,而且在实践中(由于是非随机访问的)必然会“慢”。

Nathan's solution似乎是最佳选择,因为您不想简单地找到下界,而要找到它与容器“起点”的距离。

答案 3 :(得分:1)

您可以使用以下代码:

#include <algorithm>
#include <set>
#include <iostream>

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 );
    std::size_t dist = std::distance(found, mySet.end());
    std::cout << "Number of lower bound elements: " << dist << std::endl;
}

答案 4 :(得分:1)

扩展所有现有答案 - 您始终可以编写自己的operator-

template<class T, class = typename 
    std::enable_if<
    std::is_same<
    typename T::iterator_category,
    std::bidirectional_iterator_tag
>::value>::type>
typename std::iterator_traits<T>::difference_type operator-(const T& a, const T& b)
{
    return std::distance(b, a);
}