【问题标题】:C++ sort on vector using function objectC++ 使用函数对象对向量进行排序
【发布时间】:2011-10-15 13:49:37
【问题描述】:

我正在尝试使用另一个向量 v2 对向量 v1 进行排序。我无法解决这个错误:

在抛出 'std::out_of_range' 的实例后调用终止
what(): 向量::_M_range_check
中止陷阱

在运行此代码时:

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

class Comp
{
    public:
        Comp(vector<double>& inVec): _V(inVec) {}
        bool operator()(int i, int j) {return (_V.at(i)<_V.at(j));}
    private:
        vector<double> _V;
};

int main(int argc, char** argv)
{
    double x1[] = {90.0, 100.0, 80.0};
    double x2[] = {9.0, 3.0, 1.0};
    vector<double> v1(x1,x1+3);
    vector<double> v2(x2,x2+3);

    sort(v1.begin(), v1.end(), Comp(v2));  // sort v1 according to v2

    for(unsigned int i=0; i<v1.size(); i++)
    {
        cout << v1.at(i) << " " << v2.at(i) << endl;
    }

    return 0;
}

v1v2 大小相同。为什么会出现out_of_range 错误?

提前感谢您的任何指点。

【问题讨论】:

  • 你的 Comp 类应该持有对向量的引用,而不是复制它。

标签: c++ algorithm sorting stl vector


【解决方案1】:

我相信你的问题出在这一行:

bool operator()(int i, int j) {return (_V.at(i)<_V.at(j));}

问题在于,当std::sort 算法使用自定义回调时,它会传入存储在vector 中特定位置的实际值,而不是@ 内这些位置的索引 987654324@。结果,当你调用

sort(v1.begin(), v1.end(), Comp(v2));  // sort v1 according to v2

您编写的Comp 比较器将作为参数传递存储在v1 向量中的值,然后尝试将这些位置索引到v2 向量中。由于v1 中的值大于v2 的大小,所以调用_V.at(i) 会引发out_of_range 异常。

如果您想对两个范围进行相互排序,则需要采用不同的方法。我不知道有什么直接的方法可以做到这一点,但如果我想到一个,我会告诉你的。

【讨论】:

    【解决方案2】:

    v1 的大小只是3,但您使用v2 的每个值作为v1 的索引。由于v2 有一个值9 大于v1 的大小,这就是导致std::out_of_range 错误的原因:

    bool operator()(int i, int j) {return (_V.at(i)<_V.at(j));}
    

    std::vector::at 函数给出std::out_of_range 异常,作为参数传递给它的索引大于向量的大小。即索引必须小于vector::size()

    【讨论】:

      【解决方案3】:

      好的,现在您可能已经意识到,ijvector 中保存的实际值,而不是索引。有一个很好的理由:排序是关于值的,而不是索引。请注意,您将迭代器传递给 sort 方法,因此它无法为您提取索引。当然,您可以获得相对于第一个迭代器的索引,但没有理由这样做。

      但是,让我们发疯一会儿,想象一下您会在比较器中获得索引而不是值。假设您的代码执行您想要的操作,让我们考虑以下场景:

      v1 = {20,10}; v2 = {2,1}

      我偷偷假设你想要以下输出:

      v1 = {10, 20}

      对吗?现在想象一下,我是您正在调用的排序函数,我执行以下步骤:

      • v2[0] &lt; v2[1] 是假的,所以 swap(&amp;v1[0], &amp;v1[1])

      它是排序的,不是吗?但是等等,我是一个疯狂的排序函数,所以我想确保它是排序的,所以我做了以下事情:

      • v2[0] &lt; v2[1] 是假的,swap(&amp;v1[0], &amp;v1[1])

      再说一遍:

      • v2[0] &lt; v2[1] 是假的,swap(&amp;v1[0], &amp;v1[1])

      又一次,又一次,又一次……

      你能看出问题吗?排序功能有一些要求,并且您肯定会打破基本要求。

      我怀疑您需要完全不同的容器(可能是 std::map,键来自 vec1,值来自 vec2)或至少类似于 vector&lt; pair&lt;double, double&gt; &gt;,因此您可以轻松地按第一个值或第二个值排序。如果不是,请考虑使用[0, v2.size()) 范围内的值创建vector,使用比较器对其进行排序(值等于索引,所以没关系),然后从v1 打印正确的值。此代码工作正常:

      vector<size_t> indices;
      for(size_t i =0; i < v1.size(); ++i)
      {
        indices.push_back(i);
      }
      
      // yes, it works using your original comparator
      sort(indices.begin(), indices.end(), Comp(v2));
      
      for(size_t i =0; i < indices.size(); ++i)
      {
          cout << v1.at(indices[i]) << " " << v2.at(indices[i]) << endl;
      }
      

      【讨论】:

      • +1 表示真正的原因,而不是仅仅说 std::sort 使用值而不是索引。
      【解决方案4】:

      就像在其他答案中所说,问题在于排序算法传递实际值来比较而不是索引。

      解决方法如下:

      #include <iostream>
      #include <vector>
      #include <algorithm>
      
      using namespace std;
      
      typedef pair<double, double> Zipped; // Represent an element of two lists
                                           // "zipped" together
      
      // Compare the second values of two pairs
      bool compareSeconds ( Zipped i, Zipped j )
      {
          return i.second < j.second;
      }
      
      int main ( int argc, char **argv )
      {
          double x1[] = { 90, 100, 80 };
          double x2[] = { 9, 3, 1 };
      
          vector<double> v1(x1, x1 + 3);
          vector<double> v2(x2, x2 + 3);
      
          vector<Zipped> zipped(v1.size()); // This will be the zipped form of v1
                                            // and v2
      
          for ( int i = 0; i < zipped.size(); ++i )
          {
              zipped[i] = Zipped(v1[i], v2[i]);
          }
      
          sort(zipped.begin(), zipped.end(), &compareSeconds);
      
          for ( int i = 0; i < zipped.size(); ++i )
          {
              cout << zipped[i].first << " " << zipped[i].second << endl;
          }
      
          for ( int i = 0; i < v1.size(); ++i )
          {
              v1[i] = zipped[i].first;
          }
      
          // At this point, v1 is sorted according to v2
      
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多