Come faccio a ordinare un vettore di coppie in base al secondo elemento della coppia?


133

Se ho un vettore di coppie:

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

Esiste un modo semplice per ordinare l'elenco in ordine crescente in base al secondo elemento della coppia?

So di poter scrivere un piccolo oggetto funzione che farà il lavoro, ma c'è un modo per usare parti esistenti dell'STL e std::lessfare direttamente il lavoro?

EDIT: Capisco che posso scrivere una funzione o classe separata per passare al terzo argomento da ordinare. La domanda è se posso costruirlo o meno con roba standard. Vorrei davvero qualcosa che assomiglia:

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


1
c ++ non ha i lama, quindi non puoi fare esattamente quello che vuoi, dovrai creare una funzione / funzione separata. Questo può essere un one-liner quindi non dovrebbe essere un grosso problema.
Evan Teran,

1
Il C ++ ha lambda ora! Corteggiare!
David Poole,

Risposte:


212

EDIT : usando c ++ 14, la soluzione migliore è molto semplice da scrivere grazie a lambdas che ora possono avere parametri di tipo auto. Questa è la mia attuale soluzione preferita

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

Basta usare un comparatore personalizzato (è un terzo argomento opzionale per 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());

Se stai usando un compilatore C ++ 11, puoi scrivere lo stesso usando lambdas:

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

EDIT : in risposta alle tue modifiche alla tua domanda, ecco alcuni pensieri ... se vuoi davvero essere creativo ed essere in grado di riutilizzare molto questo concetto, basta creare un modello:

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

allora puoi farlo anche tu:

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

o anche

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

Per essere onesto, questo è un po 'eccessivo, basta scrivere la funzione a 3 righe e finirla :-P


Tieni presente che questo è diverso da operator<in pair<T1,T2>. Il comparatore predefinito utilizza sia il primo che il secondo elemento (nel caso in cui i primi siano uguali). Qui viene utilizzato solo il secondo.
Googol,

@Googol: Questo è esattamente ciò che l'OP ha richiesto ... Ha detto: "is there and easy way to sort the list in increasing order based on the second element of the pair?"
Evan Teran,

@ evan-teran, sì, lo so. Stavo solo indicando che se entrambi gli elementi dei secondi sono uguali, il risultato può essere confuso (se utilizzato per l'ordinamento, ad esempio). Il comparatore predefinito non presenta questo problema perché utilizza il secondo elemento per il tie-breaking. Ho raggiunto questa domanda alla ricerca di un comparatore che utilizzava il secondo elemento come informazione principale per il confronto, ma avevo anche bisogno che usasse il primo come pareggio, quindi vorrei evitare che altri perdessero quel punto (mentre io, in fatto, fatto).
Googol,

71

Puoi usare boost in questo modo:

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

Non conosco un modo standard per farlo allo stesso modo breve e conciso, ma puoi boost::bindcapire che è tutto composto da intestazioni.


1
+1 per l'utilizzo di Boost. A proposito, con un moderno compilatore probabilmente potresti già sostituire boost con std :: tr1 poiché questo sarà presto nello standard.
Andreas Magnusson,

sfortunatamente, ho provato lo stesso con c ++ 1x std :: bind di gcc trunk, ma non è riuscito perché non ha l'opzione <per bind. non so se ciò che c ++ 1x dice a riguardo. probabilmente ti dice di usare lambda per questo :)
Johannes Schaub - litb

1
Suppongo che il boost non sia standard, ma sia abbastanza vicino. :-)
David Norman,

Pubblicato un follow-up domande a questa risposta qui: stackoverflow.com/q/4184917/220636
nabulke

34

È piuttosto semplice utilizzare la funzione di ordinamento dall'algoritmo e aggiungere la propria funzione di confronto

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

Ora devi fare il confronto in base alla seconda selezione, quindi dichiara "myComparison" come

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

5
Semplice e "al punto". Non necessita di boost o di una versione C ++ specifica. +1
Thomio

1
Questo dovrebbe essere contrassegnato come la migliore soluzione. Non ha bisogno di c ++ 14 per implementarlo.
Kartik Chauhan,

Puoi spiegarmi come funziona questo confronto? Stiamo passando due elementi a myComparision alla volta, quindi come è possibile ordinare? Inoltre, quale ruolo svolge a.second <b.second?
era s'q

30

Con C ++ 0x possiamo usare le funzioni lambda:

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

In questo esempio il tipo restituito boolviene dedotto implicitamente.

Tipi di ritorno Lambda

Quando una funzione lambda ha una singola istruzione e questa è un'istruzione return, il compilatore può dedurre il tipo restituito. Da C ++ 11, §5.1.2 / 4:

...

  • Se l'istruzione compound ha la forma { return expression ; }del tipo dell'espressione restituita dopo la conversione da valore in valore (4.1), la conversione da array a puntatore (4.2) e la conversione da funzione a puntatore (4.3);
  • in caso contrario, void.

Per specificare esplicitamente il tipo restituito utilizzare il modulo []() -> Type { }, come in:

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
Perché if (lhs.second == 0)?
il maiale il

Nessun significato particolare; lhs.second < rhs.secondpuò tornare trueo falsee il compilatore può chiaramente dedurre bool. Volevo solo dimostrare il []() -> Type { }caso.
Andreas Spindler,

Almeno con clang, questa deduzione implicita potrebbe non funzionare correttamente, ho dovuto aggiungere -> bool come tipo di ritorno lambda per farlo funzionare correttamente.
MoDJ,

5

Per qualcosa di riutilizzabile:

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

Puoi usarlo come

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

o

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


-1

Prova a scambiare gli elementi delle coppie in modo da poter usare std::sort()normalmente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.