Як сортувати вектор пар на основі другого елемента пари?


133

Якщо у мене вектор пар:

std::vector<std::pair<int, int> > vec;

Чи існує простий спосіб сортування списку в порядку зростання, виходячи з другого елемента пари?

Я знаю, що можу написати невеликий об’єкт функції, який буде виконувати роботу, але чи є спосіб використовувати існуючі частини STL та std::lessвиконувати роботу безпосередньо?

EDIT: Я розумію, що можу написати окрему функцію або клас, щоб перейти до третього аргументу для сортування. Питання полягає в тому, чи можу я це зробити зі стандартних речей. Я б дійсно щось виглядав так:

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


1
c ++ не має lamdas, тому ви не можете робити саме те, що хочете, вам потрібно створити окрему функцію / функтор. Це може бути однолінійний, так що це дійсно не повинно бути великою справою.
Еван Теран

1
У C ++ зараз є лямбда! Ву!
Девід Пул

Відповіді:


212

EDIT : використовуючи c ++ 14, найкраще рішення дуже легко написати, завдяки лямбдам, які тепер можуть мати параметри типу auto. Це моє улюблене рішення

std::sort(v.begin(), v.end(), [](auto &left, auto &right) {
    return left.second < right.second;
});

Просто використовуйте спеціальний компаратор (це необов'язковий 3-й аргумент std::sort)

struct sort_pred {
    bool operator()(const std::pair<int,int> &left, const std::pair<int,int> &right) {
        return left.second < right.second;
    }
};

std::sort(v.begin(), v.end(), sort_pred());

Якщо ви використовуєте компілятор C ++ 11, ви можете написати те ж саме, використовуючи лямбда:

std::sort(v.begin(), v.end(), [](const std::pair<int,int> &left, const std::pair<int,int> &right) {
    return left.second < right.second;
});

EDIT : у відповідь на ваші зміни на ваше запитання, ось кілька думок ... якщо ви дійсно хочете проявити творчість і вмієте багаторазово використовувати це поняття, просто створіть шаблон:

template <class T1, class T2, class Pred = std::less<T2> >
struct sort_pair_second {
    bool operator()(const std::pair<T1,T2>&left, const std::pair<T1,T2>&right) {
        Pred p;
        return p(left.second, right.second);
    }
};

тоді ви можете це зробити також:

std::sort(v.begin(), v.end(), sort_pair_second<int, int>());

або навіть

std::sort(v.begin(), v.end(), sort_pair_second<int, int, std::greater<int> >());

Хоча, чесно кажучи, все це трохи непосильне, просто напишіть функцію 3 рядків і виконайте з нею :-P


Майте на увазі, що це відрізняється від operator<в pair<T1,T2>. Порівняльник за замовчуванням використовує як перший, так і другий елемент (у випадку, коли перші рівні). Тут використовується лише другий.
Гуголь

@Googol: Ось саме про це просила ОП ... Він сказав: "is there and easy way to sort the list in increasing order based on the second element of the pair?"
Еван Теран

@ evan-teran, так, я знаю. Я вказував лише на те, що якщо обидва секундні елементи рівні, то результат може бути заплутаним (наприклад, для сортування, наприклад). Ця проблема не постраждала у порівнянні за замовчуванням, оскільки вона використовує другий елемент для розриву зв'язку. Я дійшов до цього питання, шукаючи компаратор, який використовував другий елемент як основну інформацію для порівняння, але мені також потрібно, щоб він використовував перший для розриву зв’язків, тому я хотів би уникати, щоб інші пропустили цю точку (як я, у факт, зробив).
Гуголь

71

Ви можете використовувати прискорення так:

std::sort(a.begin(), a.end(), 
          boost::bind(&std::pair<int, int>::second, _1) <
          boost::bind(&std::pair<int, int>::second, _2));

Я не знаю стандартного способу зробити це однаково коротко і стисло, але ви можете зрозуміти boost::bind, що все це складається із заголовків.


1
+1 за використання Boost. Btw, за допомогою сучасного компілятора ви, ймовірно, вже можете замінити boost на std :: tr1, оскільки це буде в стандарті незабаром.
Андреас Магнуссон

на жаль, я спробував те ж саме з c ++ 1x std :: bind gcc, і це не вдалося, оскільки у нього немає опції <bind. не знаю, чи говорить про це c ++ 1x. напевно, це говорить вам про те, щоб використовувати лямбда для цього :)
Йоханнес Шауб - ліб

1
Я думаю, що підвищення не є стандартним, але воно досить близько. :-)
Девід Норман

Вивісив Followup питання ця відповідь тут: stackoverflow.com/q/4184917/220636
nabulke

34

Ви досить просто використовуєте функцію сортування за алгоритмом та додаєте власну функцію порівняння

vector< pair<int,int > > v;
sort(v.begin(),v.end(),myComparison);

Тепер ви повинні зробити порівняння на основі другого вибору, щоб оголосити вас "моїм порівнянням" як

bool myComparison(const pair<int,int> &a,const pair<int,int> &b)
{
       return a.second<b.second;
}

5
Простий і "в точку". Не потрібно підсилення чи певної версії C ++. +1
Томіо

1
Це слід відзначити як найкраще рішення. Для його реалізації не потрібно c ++ 14.
Картік Чаухан

Чи можете ви пояснити мені, як працює це порівняння? Ми передаємо два елементи в myComparision одночасно, то як це можна сортувати? Також яку роль відіграє a.second <b.second?
era s'q

30

За допомогою C ++ 0x ми можемо використовувати лямбда-функції:

using namespace std;
vector<pair<int, int>> v;
        .
        .
sort(v.begin(), v.end(),
     [](const pair<int, int>& lhs, const pair<int, int>& rhs) {
             return lhs.second < rhs.second; } );

У цьому прикладі вид повернення boolнеявно виводиться.

Типи повернення лямбда

Коли в лямбда-функції є одне твердження, а це оператор return, компілятор може вивести тип повернення. З C ++ 11, §5.1.2 / 4:

...

  • Якщо складене висловлення має вигляд { return expression ; }типу повернутого виразу після перетворення значення-в-значення (4.1), перетворення масиву на вказівник (4.2) та перетворення функції на вказівник (4.3);
  • в іншому випадку void.

Щоб чітко вказати тип повернення, використовуйте форму []() -> Type { }, наприклад у:

sort(v.begin(), v.end(),
     [](const pair<int, int>& lhs, const pair<int, int>& rhs) -> bool {
             if (lhs.second == 0)
                 return true;
             return lhs.second < rhs.second; } );

1
Чому if (lhs.second == 0)?
свиня

Немає особливого значення; lhs.second < rhs.secondможе повернутися trueабо, falseі компілятор може чітко вивести bool. Просто хотів продемонструвати []() -> Type { }справу.
Андреас Шпіндлер

Принаймні з клангом, ця неявна дедукція може не працювати належним чином, мені довелося додати -> bool як тип повернення лямбда, щоб нормально працювати.
MoDJ

5

Для чогось багаторазового використання:

template<template <typename> class P = std::less >
struct compare_pair_second {
    template<class T1, class T2> bool operator()(const std::pair<T1, T2>& left, const std::pair<T1, T2>& right) {
        return P<T2>()(left.second, right.second);
    }
};

Ви можете використовувати його як

std::sort(foo.begin(), foo.end(), compare_pair_second<>());

або

std::sort(foo.begin(), foo.end(), compare_pair_second<std::less>());


-1

Спробуйте поміняти елементи пар, щоб ви могли використовувати їх std::sort()як звичайні.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.