C ++使用函数对象对向量进行排序

时间:2011-07-28 23:42:59

标签: c++ algorithm sorting stl vector

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

  

在抛出'std :: out_of_range'实例后终止调用   what():vector :: _ 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错误?

提前感谢任何指示。

4 个答案:

答案 0 :(得分:9)

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

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

问题是当std::sort算法使用自定义回调时,它会传递存储在vector特定位置的实际值,而不是那些索引 vector内的位置。结果,当你打电话

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

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

如果要相对于彼此对两个范围进行排序,则需要采用不同的方法。我不知道这样做的直接方式,但如果我想到一个,我会告诉你的。

答案 1 :(得分:6)

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()

答案 2 :(得分: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;
}

答案 3 :(得分:4)

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

然而,让我们疯狂一段时间,并想象你会在比较器中得到指数而不是值。假设您的代码符合您的要求,让我们考虑以下场景:

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

我暗地里假设你想要以下输出:

v1 = {10, 20}

正确?现在假设我是一个你正在调用的排序函数,我按照以下步骤操作:

  • v2[0] < v2[1]为false,因此swap(&v1[0], &v1[1])

它已经分类了,不是吗?但是等等,我是一个疯狂的排序函数,所以我想确保它已经排序,所以我做了以下几点:

  • v2[0] < v2[1]为false,swap(&v1[0], &v1[1])

再次:

  • v2[0] < v2[1]为false,swap(&v1[0], &v1[1])

再一次又一次......

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

我怀疑你需要完全不同的容器(可能std::map使用来自vec1的键和来自vec2的值)或至少类似vector< pair<double, double> >的容器,因此您可以轻松排序通过第一或第二值。如果没有,请考虑使用范围vector中的值创建[0, v2.size()),使用比较器对其进行排序(值等于索引,这样就可以了),然后从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;
}