对自定义对象的矢量进行排序

时间:2009-09-04 17:05:37

标签: c++ stl sorting

如何对包含自定义(即用户定义)对象的矢量进行排序 可能会使用标准STL算法 sort 以及谓词(函数或函数对象),它将在自定义对象中的一个字段(作为排序键)上运行。 /> 我是在正确的轨道上吗?

14 个答案:

答案 0 :(得分:327)

使用std::sort

的简单示例
struct MyStruct
{
    int key;
    std::string stringValue;

    MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
};

struct less_than_key
{
    inline bool operator() (const MyStruct& struct1, const MyStruct& struct2)
    {
        return (struct1.key < struct2.key);
    }
};

std::vector < MyStruct > vec;

vec.push_back(MyStruct(4, "test"));
vec.push_back(MyStruct(3, "a"));
vec.push_back(MyStruct(2, "is"));
vec.push_back(MyStruct(1, "this"));

std::sort(vec.begin(), vec.end(), less_than_key());

编辑正如Kirill V. Lyadvinsky指出的那样,您可以为operator<实施MyStruct而不是提供排序谓词:

struct MyStruct
{
    int key;
    std::string stringValue;

    MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}

    bool operator < (const MyStruct& str) const
    {
        return (key < str.key);
    }
};

使用此方法意味着您可以按如下方式对矢量进行简单排序:

std::sort(vec.begin(), vec.end());

Edit2:正如Kappa建议您还可以通过重载>运算符并稍微更改排序调用来按降序对向量进行排序:

struct MyStruct
{
    int key;
    std::string stringValue;

    MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}

    bool operator > (const MyStruct& str) const
    {
        return (key > str.key);
    }
};

你应该将sort称为:

std::sort(vec.begin(), vec.end(),greater<MyStruct>());

答案 1 :(得分:141)

为了保险范围。我使用lambda expressions提出了一个实现。

<强> C ++ 11

#include <vector>
#include <algorithm>

using namespace std;

vector< MyStruct > values;

sort( values.begin( ), values.end( ), [ ]( const MyStruct& lhs, const MyStruct& rhs )
{
   return lhs.key < rhs.key;
});

<强> C ++ 14

#include <vector>
#include <algorithm>

using namespace std;

vector< MyStruct > values;

sort( values.begin( ), values.end( ), [ ]( const auto& lhs, const auto& rhs )
{
   return lhs.key < rhs.key;
});

答案 2 :(得分:52)

您可以将functor用作std::sort的第三个参数,或者您可以在班级中定义operator<

struct X {
    int x;
    bool operator<( const X& val ) const { 
        return x < val.x; 
    }
};

struct Xgreater
{
    bool operator()( const X& lx, const X& rx ) const {
        return lx.x < rx.x;
    }
};

int main () {
    std::vector<X> my_vec;

    // use X::operator< by default
    std::sort( my_vec.begin(), my_vec.end() );

    // use functor
    std::sort( my_vec.begin(), my_vec.end(), Xgreater() );
}

答案 3 :(得分:13)

你走在正确的轨道上。默认情况下,std::sort将使用operator<作为比较功能。因此,为了对对象进行排序,您必须重载bool operator<( const T&, const T& )或提供进行比较的仿函数,如下所示:

 struct C {
    int i;
    static bool before( const C& c1, const C& c2 ) { return c1.i < c2.i; }
 };

 bool operator<( const C& c1, const C& c2 ) { return c1.i > c2.i; }

 std::vector<C> values;

 std::sort( values.begin(), values.end() ); // uses operator<
 std::sort( values.begin(), values.end(), C::before );

使用仿函数的好处是你可以使用一个函数来访问类的私有成员。

答案 4 :(得分:11)

使用各种方法可以实现对vector或任何其他适用的(可变输入迭代器)类型的X类型的自定义对象进行排序,尤其是包括使用标准库算法,如

由于大多数技术,为了获得X元素的相对排序,已经发布,我将从&#34;为什么&#34;和#34;当&#34;使用各种方法。

&#34; best&#34;方法将取决于不同的因素:

  1. X对象的排序范围是一项常见或罕见的任务(这些范围是在程序中的多个不同位置还是由库用户排序)?
  2. 是否需要排序&#34;自然&#34; (预期)或者有多种方法可以将这种类型与自身进行比较?
  3. 性能是一个问题,还是X对象的排序范围应该是万无一失的?
  4. 如果X的排序范围是一项常见任务,并且预期实现排序(即X只包含一个基本值),那么on可能会导致重载operator<因为它可以在没有任何模糊的情况下进行排序(比如正确传递适当的比较器)并反复产生预期结果。

    如果排序是一项常见任务或可能在不同的上下文中需要,但有多个条件可用于对X个对象进行排序,我会选择Functors(重载{{1}自定义类的函数)或函数指针(即一个用于词法排序的函子/函数和另一个用于自然排序的函数)。

    如果类型operator()的排序范围在其他上下文中不常见或不太可能,我倾向于使用lambdas而不是使任何具有更多函数或类型的命名空间混乱。

    如果排序不清晰,则尤其如此。或者&#34;自然&#34;某种程度上来说。在查看就地应用的lambda时,您可以轻松获得排序背后的逻辑,而X乍一看是不透明的,您必须查看定义以了解排序逻辑将是什么应用

    但请注意,单个operator<定义是单点故障,而多个lambas是多个故障点,需要更加谨慎。

    如果operator<的定义在排序完成/编译排序模板时不可用,编译器可能会在比较对象时强制进行函数调用,而不是内联排序逻辑这可能是一个严重的缺点(至少在没有应用链接时间优化/代码生成时)。

    为了使用标准库排序算法而实现operator<的可比性的方法

    允许class Xstd::vector<X> vec_X;

    1。重载std::vector<Y> vec_Y;T::operator<(T)并使用不期望比较功能的标准库模板。

    重载成员operator<(T, T)

    operator<

    或免费struct X { int i{}; bool operator<(X const &r) const { return i < r.i; } }; // ... std::sort(vec_X.begin(), vec_X.end());

    operator<

    2。使用带有自定义比较函数的函数指针作为排序函数参数。

    struct Y {
      int j{}; 
    };
    bool operator<(Y const &l, Y const &r) { return l.j < r.j; }
    // ...
    std::sort(vec_Y.begin(), vec_Y.end());
    

    3。为自定义类型创建struct X { int i{}; }; bool X_less(X const &l, X const &r) { return l.i < r.i; } // ... std::sort(vec_X.begin(), vec_X.end(), &X_less); 重载,可以将其作为比较函数传递。

    bool operator()(T, T)

    使用C ++ 11和模板可以更加通用地编写这些函数对象定义:

    struct X {
      int i{};  
      int j{};
    };
    struct less_X_i
    {
        bool operator()(X const &l, X const &r) const { return l.i < r.i; }
    };
    struct less_X_j
    {
        bool operator()(X const &l, X const &r) const { return l.j < r.j; }
    };
    // sort by i
    std::sort(vec_X.begin(), vec_X.end(), less_X_i{});
    // or sort by j
    std::sort(vec_X.begin(), vec_X.end(), less_X_j{});
    

    可用于对支持struct less_i { template<class T, class U> bool operator()(T&& l, U&& r) const { return std::forward<T>(l).i < std::forward<U>(r).i; } }; 的成员i的任何类型进行排序。

    4。将anonymus closure(lambda)作为比较参数传递给排序函数。

    <

    C ++ 14支持更通用的lambda表达式:

    struct X {
      int i{}, j{};
    };
    std::sort(vec_X.begin(), vec_X.end(), [](X const &l, X const &r) { return l.i < r.i; });
    

    可以用宏包裹

    std::sort(a.begin(), a.end(), [](auto && l, auto && r) { return l.i < r.i; });
    

    使普通的比较器创建非常顺利:

    #define COMPARATOR(code) [](auto && l, auto && r) -> bool { return code ; }
    

答案 5 :(得分:4)

是的,带有第三个参数(函数或对象)的std::sort()会更容易。一个例子: http://www.cplusplus.com/reference/algorithm/sort/

答案 6 :(得分:3)

在你的课堂上,你可能会重载“&lt;”操作

class MyClass
{
  bool operator <(const MyClass& rhs)
  {
    return this->key < rhs.key;
  }
}

答案 7 :(得分:2)

以下是使用lambdas

的代码
#include "stdafx.h"
#include <vector>
#include <algorithm>

using namespace std;

struct MyStruct
{
    int key;
    std::string stringValue;

    MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
};

int main()
{
    std::vector < MyStruct > vec;

    vec.push_back(MyStruct(4, "test"));
    vec.push_back(MyStruct(3, "a"));
    vec.push_back(MyStruct(2, "is"));
    vec.push_back(MyStruct(1, "this"));

    std::sort(vec.begin(), vec.end(), 
        [] (const MyStruct& struct1, const MyStruct& struct2)
        {
            return (struct1.key < struct2.key);
        }
    );
    return 0;
}

答案 8 :(得分:2)

我很好奇是否可以调用std :: sort的各种方式对性能产生任何可测量的影响,所以我创建了这个简单的测试:

$ cat sort.cpp
#include<algorithm>
#include<iostream>
#include<vector>
#include<chrono>

#define COMPILER_BARRIER() asm volatile("" ::: "memory");

typedef unsigned long int ulint;

using namespace std;

struct S {
  int x;
  int y;
};

#define BODY { return s1.x*s2.y < s2.x*s1.y; }

bool operator<( const S& s1, const S& s2 ) BODY
bool Sgreater_func( const S& s1, const S& s2 ) BODY

struct Sgreater {
  bool operator()( const S& s1, const S& s2 ) const BODY
};

void sort_by_operator(vector<S> & v){
  sort(v.begin(), v.end());
}

void sort_by_lambda(vector<S> & v){
  sort(v.begin(), v.end(), []( const S& s1, const S& s2 ) BODY );
}

void sort_by_functor(vector<S> &v){
  sort(v.begin(), v.end(), Sgreater());
}

void sort_by_function(vector<S> &v){
  sort(v.begin(), v.end(), &Sgreater_func);
}

const int N = 10000000;
vector<S> random_vector;

ulint run(void foo(vector<S> &v)){
  vector<S> tmp(random_vector);
  foo(tmp);
  ulint checksum = 0;
  for(int i=0;i<tmp.size();++i){
     checksum += i *tmp[i].x ^ tmp[i].y;
  }
  return checksum;
}

void measure(void foo(vector<S> & v)){

ulint check_sum = 0;

  // warm up
  const int WARMUP_ROUNDS = 3;
  const int TEST_ROUNDS = 10;

  for(int t=WARMUP_ROUNDS;t--;){
    COMPILER_BARRIER();
    check_sum += run(foo);
    COMPILER_BARRIER();
  }

  for(int t=TEST_ROUNDS;t--;){
    COMPILER_BARRIER();
    auto start = std::chrono::high_resolution_clock::now();
    COMPILER_BARRIER();
    check_sum += run(foo);
    COMPILER_BARRIER();
    auto end = std::chrono::high_resolution_clock::now();
    COMPILER_BARRIER();
    auto duration_ns = std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count();

    cout << "Took " << duration_ns << "s to complete round" << endl;
  }

  cout << "Checksum: " << check_sum << endl;
}

#define M(x) \
  cout << "Measure " #x " on " << N << " items:" << endl;\
  measure(x);

int main(){
  random_vector.reserve(N);

  for(int i=0;i<N;++i){
    random_vector.push_back(S{rand(), rand()});
  }

  M(sort_by_operator);
  M(sort_by_lambda);
  M(sort_by_functor);
  M(sort_by_function);
  return 0;
}

它的作用是创建一个随机向量,然后测量复制它所需的时间并对其副本进行排序(并计算一些校验和以避免过于强烈的死代码消除)。

我正在使用g ++(GCC)7.2.1 20170829(Red Hat 7.2.1-1)进行编译

$ g++ -O2 -o sort sort.cpp && ./sort

以下是结果:

Measure sort_by_operator on 10000000 items:
Took 0.994285s to complete round
Took 0.990162s to complete round
Took 0.992103s to complete round
Took 0.989638s to complete round
Took 0.98105s to complete round
Took 0.991913s to complete round
Took 0.992176s to complete round
Took 0.981706s to complete round
Took 0.99021s to complete round
Took 0.988841s to complete round
Checksum: 18446656212269526361
Measure sort_by_lambda on 10000000 items:
Took 0.974274s to complete round
Took 0.97298s to complete round
Took 0.964506s to complete round
Took 0.96899s to complete round
Took 0.965773s to complete round
Took 0.96457s to complete round
Took 0.974286s to complete round
Took 0.975524s to complete round
Took 0.966238s to complete round
Took 0.964676s to complete round
Checksum: 18446656212269526361
Measure sort_by_functor on 10000000 items:
Took 0.964359s to complete round
Took 0.979619s to complete round
Took 0.974027s to complete round
Took 0.964671s to complete round
Took 0.964764s to complete round
Took 0.966491s to complete round
Took 0.964706s to complete round
Took 0.965115s to complete round
Took 0.964352s to complete round
Took 0.968954s to complete round
Checksum: 18446656212269526361
Measure sort_by_function on 10000000 items:
Took 1.29942s to complete round
Took 1.3029s to complete round
Took 1.29931s to complete round
Took 1.29946s to complete round
Took 1.29837s to complete round
Took 1.30132s to complete round
Took 1.3023s to complete round
Took 1.30997s to complete round
Took 1.30819s to complete round
Took 1.3003s to complete round
Checksum: 18446656212269526361

看起来除了传递函数指针之外的所有选项都非常相似,并且传递函数指针会导致+ 30%的惩罚。

它看起来像运营商&lt;版本慢了〜1%(我多次重复测试并且效果仍然存在),这有点奇怪,因为它表明生成的代码不同(我缺乏分析--save-temps输出的技能)。

答案 9 :(得分:1)

    // sort algorithm example
    #include <iostream>     // std::cout
    #include <algorithm>    // std::sort
    #include <vector>       // std::vector
    using namespace std;
    int main () {
        char myints[] = {'F','C','E','G','A','H','B','D'};
        vector<char> myvector (myints, myints+8);               // 32 71 12 45 26 80 53 33
        // using default comparison (operator <):
        sort (myvector.begin(), myvector.end());           //(12 32 45 71)26 80 53 33
        // print out content:
        cout << "myvector contains:";
        for (int i=0; i!=8; i++)
            cout << ' ' <<myvector[i];
        cout << '\n';
        system("PAUSE");
    return 0;
    }

答案 10 :(得分:1)

您可以使用用户定义的比较器类。

class comparator
{
    int x;
    bool operator()( const comparator &m,  const comparator &n )
    { 
       return m.x<n.x;
    }
 }

答案 11 :(得分:0)

要对矢量进行排序,您可以使用。(/ p>)中的sort()算法

sort(vec.begin(),vec.end(),less<int>());

使用的第三个参数可以更大或更小,或者也可以使用任何函数或对象。但是,默认运算符是&lt;如果你把第三个参数留空。

// using function as comp
std::sort (myvector.begin()+4, myvector.end(), myfunction);
bool myfunction (int i,int j) { return (i<j); }

// using object as comp
std::sort (myvector.begin(), myvector.end(), myobject);

答案 12 :(得分:0)

typedef struct Freqamp{
    double freq;
    double amp;
}FREQAMP;

bool struct_cmp_by_freq(FREQAMP a, FREQAMP b)
{
    return a.freq < b.freq;
}

main()
{
    vector <FREQAMP> temp;
    FREQAMP freqAMP;

    freqAMP.freq = 330;
    freqAMP.amp = 117.56;
    temp.push_back(freqAMP);

    freqAMP.freq = 450;
    freqAMP.amp = 99.56;
    temp.push_back(freqAMP);

    freqAMP.freq = 110;
    freqAMP.amp = 106.56;
    temp.push_back(freqAMP);

    sort(temp.begin(),temp.end(), struct_cmp_by_freq);
}

如果compare为false,它将执行&#34; swap&#34;。

答案 13 :(得分:0)

在C ++ 20中,无需用户定义的比较器就可以使用默认运算符<=>。编译器会解决这个问题。

#include <iostream>
#include <compare>
#include <vector>
#include <algorithm>

struct MyInt
{
    int value;
    MyInt(int val) : value(val) {}
    auto operator<=>(const MyInt& other) const = default;
};


int main()
{
    MyInt Five(5);
    MyInt Two(2);
    MyInt Six(6);
    
    std::vector V{Five, Two, Six};
    std::sort(V.begin(), V.end());
    
    for (const auto& element : V)
        std::cout << element.value << std::endl;
}

输出:

2
5
6