【问题标题】:std::set::equal_range for a container of std::pairstd::set::equal_range 用于 std::pair 的容器
【发布时间】:2012-11-11 23:00:29
【问题描述】:

我正在尝试查找包含 int 值 i 的所有范围 [a,b],其中 a set<std:pair<int,int>> 作为范围集。

在下面,在vector&lt;int&gt; 上使用相等的范围会产生范围的开始和结束之后的一个。

当我对set&lt;pair&lt;int,int&gt;&gt; 执行相同操作时,结果从范围末尾开始和结束,因此不包括包含该值的范围。

#include <set>
#include <iostream>
#include <algorithm>
using namespace std;

int main()
{
    int ia[] = {1,2,3,4,5,6,7,8,9,10};
    set<int> s1(begin(ia),end(ia));
    auto range1 = s1.equal_range(5);

    cout << *range1.first << " " << *range1.second << endl; //prints 5 6

    pair<int,int> p[] = {make_pair(1,10), 
                         make_pair(11,20),  
                         make_pair(21,30), 
                         make_pair(31,40)}; 
    set<pair<int,int>> s(begin(p), end(p));    

    auto range = s.equal_range(make_pair(12,12));

    cout << range.first->first << " " << range.first->second << endl; //prints 21 30, why?
    cout << range.second->first << " " << range.second->second << endl; //prints 21 30

}

prints
5 6
21 30
21 30
为什么set&lt;pair&lt;int,int&gt;&gt;上的equal_range不包括包围值(12)的范围,即[11.20]

【问题讨论】:

    标签: c++ c++11


    【解决方案1】:

    equal_range 的行为完全正确:

    assert( std::make_pair(11, 20) < std::make_pair(12, 12) );
    assert( std::make_pair(12, 12) < std::make_pair(21, 30) );
    

    [11,20] 不是一个范围,它是一对。这对 [12,12] 不在另一对“内”,这甚至说都没有意义。

    [12,12] 不在 [11,20] 之内,它大于它。 std::pair 的小于运算符首先比较第一个元素,只有当它们相等时才会查看第二个元素,因此对于任何 x 和 @,make_pair(11,x) 小于 make_pair(12, y) 987654328@

    所以equal_range 告诉你[12,12] 会被插入到[11,20] 之后和[21,30] 之前,这是正确的。

    如果要将对视为值的范围,则需要编写代码来执行此操作,不要假设对的内置比较可以做到这一点。您实际上是在尝试在一系列整数对中找到一个整数 12,但是已经编写了代码来在一系列整数对中找到一对 [12,12]。那不是一回事。

    【讨论】:

      【解决方案2】:

      该范围内不包括[11, 20],因为该范围内不包括任何东西[12, 12] 没有相等的元素,所以它返回一个空范围(由半开区间[x, x) 表示)。

      顺便说一句,取消引用范围的上限可能会调用未定义的行为,因为这可能等于 s.end()

      【讨论】:

      • 不应该使用与定义集合相同的less&lt;pair&lt;int,int&gt;&gt; 默认比较器吗?在这种情况下,包含 12 的范围返回 true,因为 [11,20]
      • 我想在一个包含一个值的大集合中找到所有范围。我可以使用带有 lambda 的 find_if 算法而不是使用 set 成员来测试封闭性,但这是否意味着我需要测试集合中的所有元素(数以百万计)?
      • 是否可以通过使用集合成员函数找到所有 O(log(n)) 范围?
      • @user1626720:如果您的区间重叠(并且可能有多个覆盖特定点),您需要一个区间树,而不是 std::set。如果间隔不重叠(如您的示例中的情况),您所做的几乎是正确的。您只需立即查看下方和下限。
      • 谢谢@jpalecek:这个例子并不出色,因为间隔确实重叠(和重复)。使用std::set 的原因是我希望使用stdlib 红黑树实现而不是滚动我自己的区间树。我在吠叫错误的树吗? :P
      【解决方案3】:

      [12, 12] 对排序在[11, 20] 之后和[21, 30] 之前。

      std::set::equal_range() 包含一系列 equal 元素。您的集合中没有相等的元素(尤其不是[11, 20]),所以equal_range() 返回[21, 30], [21, 30]

      【讨论】:

        【解决方案4】:

        equal_range实现为先调用lower_bound,再调用upper_bound搜索剩余数据集。

        template <class ForwardIterator, class T>
          pair<ForwardIterator,ForwardIterator>
            equal_range ( ForwardIterator first, ForwardIterator last, const T& value )
        {
          ForwardIterator it = lower_bound (first,last,value);
          return make_pair ( it, upper_bound(it,last,value) );
        }
        

        查看您的示例: 它调用lower_bound来定位value的下界(即pair(12,12),到达

        pair<int,int> p[] = {make_pair(1,10), 
                             make_pair(11,20), 
                             make_pair(21,30),   // <--- lower_bound() points to here
                             make_pair(31,40)}; 
        

        然后调用upper_bound()在(21,30),(31,40)上搜索,找不到,返回(21,30)

        http://www.cplusplus.com/reference/algorithm/upper_bound/

        【讨论】:

          【解决方案5】:

          我认为您的 std::set&lt;std::pair&lt;int, int&gt; &gt; 不会对您与整数相交有太大帮助:您可以找到 s.lower_bound(std::make_pair(i + 1, i + 1) 来切断搜索,但所有范围从低于 i + 1 的索引开始都可以如果第二个边界足够大,则可能包含值i。如果您知道范围的最大大小,在这种情况下您可以通过s.lower_bound(std::make_pair(i - max_range_size, i - max_range_size)) 将搜索限制在前面,这可能会有所帮助。您需要依次检查每个范围,以确定您的 i 是否属于它们:

          auto it    = s.lower_bound(std::make_pair(i - max_range_size, i - max_range_size));
          auto upper = s.lower_bound(std::make_pair(i + 1, i + 1));
          for (; it != upper; ++it) {
              if (i < it->second) {
                  std::cout << "range includes " << i << ": "
                            << [" << it.first << ", " << it->second << "]\n";
          }
          

          (或类似的东西......)

          【讨论】:

          • 这些范围可以重叠,所以即使我通过首先检查知道最大范围,这也不会导致对 O(n) 的改进。例如,如果范围类似于 [a [c [e... ...f] d] b]e &lt; i &lt; f
          • 是的,没错。如果你想要比 O(n) 更好的东西,你需要一个不同的算法,例如,使用range trees
          猜你喜欢
          • 2010-10-28
          • 2015-03-09
          • 1970-01-01
          • 2013-02-27
          • 2011-06-06
          • 1970-01-01
          • 2010-10-12
          • 1970-01-01
          • 2011-10-29
          相关资源
          最近更新 更多