Сортировка вектора пользовательских объектов

Как сделать сортировку вектора, содержащего пользовательские (т.е. определенные пользователем) объекты.
Вероятно, должен использоваться стандартный алгоритм STL sort, а также предикат (функция или объект функции), который будет работать на одном из полей (в качестве ключа для сортировки) в пользовательском объекте.
Я на правильном пути?

Ответ 1

Простой пример с использованием 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());

Изменить: Как указал Кирилл В. Лядвинский, вместо предоставления предиката сортировки вы можете реализовать 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: Как говорит Каппа, вы также можете отсортировать вектор в порядке убывания с помощью оператора override > и немного изменить сортировку сортировки:

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(),greater<MyStruct>());

Ответ 2

В интересах покрытия. Я предложил реализацию с помощью лямбда-выражения.

С++ 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;
});

С++ 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;
});

Ответ 3

Вы могли бы использовать функтор в качестве третьего аргумента 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() );
}

Ответ 4

Вы на правильном пути. 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 );

Преимущество использования функтора заключается в том, что вы можете использовать функцию с доступом к закрытым членам класса.

Ответ 5

Сортировка такого vector или любого другого применимого (изменяемого входного итератора) диапазона пользовательских объектов типа X может быть достигнута с использованием различных методов, в частности, включая использование стандартных библиотечных алгоритмов, таких как

Поскольку большинство методов для получения относительного упорядочения элементов X уже были опубликованы, я начну с некоторых заметок о том, почему "и почему" использовать различные подходы.

"Лучший" подход будет зависеть от разных факторов:

  • Является ли диапазон сортировки объектов X общей или редкой задачей (будут ли такие диапазоны отсортированы в разных местах программы или пользователями библиотеки)?
  • Требуется ли сортировка "естественная" (ожидается) или существует несколько способов сравнить тип с самим собой?
  • Является ли производительность проблемой или должны ли диапазоны сортировки объектов X быть надежными?

Если диапазоны сортировки X являются общей задачей, и ожидаемая сортировка должна быть ожидаемой (т.е. X просто обертывает одно фундаментальное значение), то, вероятно, произойдет перегрузка operator<, поскольку она позволяет сортировать без каких-либо fuzz (например, правильно прохождение правильных компараторов) и многократно дает ожидаемые результаты.

Если сортировка является общей задачей или может потребоваться в разных контекстах, но существует несколько критериев, которые можно использовать для сортировки объектов X, я бы пошел на функторы (перегруженные operator() функции пользовательских классов) или указатели функций (т.е. один функтор/функция для лексического упорядочения и другой для естественного упорядочения).

Если диапазоны сортировки типа X являются необычными или маловероятными в других контекстах, я стараюсь использовать lambdas вместо загромождения любого пространства имен с большим количеством функций или типов.

Это особенно верно, если сортировка в некотором роде не является "понятной" или "естественной". Вы можете легко получить логику заказа, глядя на лямбду, которая применяется на месте, тогда как operator< является на первый взгляд непротиворечивой, и вам нужно будет найти определение, чтобы знать, какая логика упорядочения будет применяться.

Обратите внимание, однако, что одно определение operator< - это единственная точка отказа, тогда как несколько ямб - это несколько точек отказа и требуют более осторожного внимания.

Если определение operator< недоступно, если сортировка выполнена/шаблон сортировки скомпилирован, компилятор может быть вынужден сделать вызов функции при сравнении объектов, вместо того, чтобы вставлять логику упорядочения, которая может быть серьезный недостаток (по крайней мере, когда оптимизация времени соединения/генерация кода не применяется).

Способы достижения сопоставимости class X для использования стандартных алгоритмов сортировки библиотек

Пусть std::vector<X> vec_X; и std::vector<Y> vec_Y;

1. Перегрузка 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<:

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

2. Используйте указатель функции с пользовательской функцией сравнения в качестве параметра функции сортировки.

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

3. Создайте перегрузку bool operator()(T, T) для настраиваемого типа, который можно передать как функтор сравнения.

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{});

Эти определения функциональных объектов могут быть написаны немного более общим с использованием С++ 11 и шаблонов:

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. Передайте закрытие анонима (лямбда) в качестве параметра сравнения для функций сортировки.

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

В тех случаях, когда С++ 14 допускает еще более общее выражение лямбда:

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

делает обычное создание компаратора вполне гладким:

// sort by i
std::sort(v.begin(), v.end(), COMPARATOR(l.i < r.i));
// sort by j
std::sort(v.begin(), v.end(), COMPARATOR(l.j < r.j));

Ответ 7

В вашем классе вы можете перегрузить символ "<" Оператор.

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

Ответ 8

Ниже приведен код с использованием 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;
}

Ответ 9

Вы можете использовать пользовательский класс компаратора.

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

Ответ 10

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

Ответ 11

Чтобы отсортировать вектор, вы можете использовать алгоритм sort().

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

Третий используемый параметр может быть больше или меньше, или можно использовать любую функцию или объект. Однако оператор по умолчанию равен < если вы оставите третий параметр пустым.

// 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

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

если сравнение ложно, оно будет "обмениваться".