¿Cómo se ordena un vector que contiene objetos personalizados (es decir, definidos por el usuario)?
Probablemente, el algoritmo estándar STL especie junto con un predicado (una función o un objeto función) que operaría en uno de los campos (como una clave para la clasificación) en el objeto personalizado se debe utilizar.
¿Estoy en el camino correcto?
248
Respuestas:
Un ejemplo simple usando
std::sort
Editar: como señaló Kirill V. Lyadvinsky, en lugar de proporcionar un predicado de clasificación, puede implementar el
operator<
paraMyStruct
:El uso de este método significa que simplemente puede ordenar el vector de la siguiente manera:
Edit2: como sugiere Kappa, también puede ordenar el vector en orden descendente sobrecargando un
>
operador y cambiando la llamada de ordenar un poco:Y deberías llamar ordenar como:
fuente
std::sort(vec.begin(), vec.end(), greater<MyStruct>())
que es limpio y elegante.#include <functional>
usar "std :: greater".operator<
y usar unostd::sort(vec.begin(), vec.end());
ostd::sort(vec.rbegin(), vec.rend());
dependiendo de si desea tener un orden ascendente o descendente.En interés de la cobertura. Presento una implementación usando expresiones lambda .
C ++ 11
C ++ 14
fuente
>
lugar de<
obtener un orden descendente.Podría usar functor como tercer argumento de
std::sort
, o podría definiroperator<
en su clase.fuente
const
al final de la firma de la función?const
.const
palabra clave al final de la firma especifica que laoperator()
función no cambia la instancia de laXgreater
estructura (que en general podría tener variables miembro), mientras que la indicaciónconst
de los valores de entrada solo especifica que esos valores de entrada son inmutables.La clasificación de tal
vector
o cualquier otro rango aplicable (iterador de entrada mutable) de objetos de tipo personalizadosX
se puede lograr usando varios métodos, especialmente incluyendo el uso de algoritmos de biblioteca estándar comosort
,stable_sort
,partial_sort
opartial_sort_copy
.Como la mayoría de las técnicas, para obtener un orden relativo de
X
elementos, ya se han publicado, comenzaré con algunas notas sobre "por qué" y "cuándo" usar los diversos enfoques.El "mejor" enfoque dependerá de diferentes factores:
X
objetos es una tarea común o rara (se ordenarán dichos rangos en diferentes lugares del programa o por los usuarios de la biblioteca)?X
objetos debe ser infalible?Si la ordenación de los rangos de
X
es una tarea común y es de esperar la ordenación lograda (es decir,X
simplemente envuelve un único valor fundamental), entonces probablemente iría por sobrecargaoperator<
ya que permite la clasificación sin ninguna confusión (como pasar correctamente los comparadores adecuados) y produce repetidamente los resultados esperados resultados.Si ordenar es una tarea común o es probable que se requiera en diferentes contextos, pero hay varios criterios que se pueden usar para ordenar
X
objetos, elegiría Functors (operator()
funciones sobrecargadas de clases personalizadas) o punteros de función (es decir, un functor / función para ordenamiento léxico y otro para ordenamiento natural).Si ordena rangos de tipo
X
es poco común o poco probable en otros contextos, tiendo a usar lambdas en lugar de saturar cualquier espacio de nombres con más funciones o tipos.Esto es especialmente cierto si la clasificación no es "clara" o "natural" de alguna manera. Puede obtener fácilmente la lógica detrás del pedido cuando mira una lambda que se aplica en el lugar, mientras que
operator<
es opaca a primera vista y tendría que buscar la definición para saber qué lógica de pedido se aplicará.Sin embargo,
operator<
tenga en cuenta que una sola definición es un único punto de falla, mientras que múltiples lambas son múltiples puntos de falla y requieren más precaución.Si la definición de
operator<
no está disponible donde se realiza la ordenación / se compila la plantilla de ordenación, el compilador podría verse obligado a realizar una llamada a la función al comparar objetos, en lugar de incluir la lógica de ordenación que podría ser un inconveniente grave (al menos cuando La optimización del tiempo de enlace / generación de código no se aplica).Formas de lograr comparabilidad
class X
para utilizar algoritmos de clasificación de bibliotecas estándarDejar
std::vector<X> vec_X;
ystd::vector<Y> vec_Y;
1. Sobrecargue
T::operator<(T)
ooperator<(T, T)
use plantillas de biblioteca estándar que no esperen una función de comparación.Cualquiera de los miembros de sobrecarga
operator<
:o gratis
operator<
:2. Utilice un puntero de función con una función de comparación personalizada como parámetro de función de clasificación.
3. Cree una
bool operator()(T, T)
sobrecarga para un tipo personalizado que se puede pasar como functor de comparación.Esas definiciones de objetos de función se pueden escribir un poco más genéricas usando C ++ 11 y plantillas:
que se puede usar para ordenar cualquier tipo con
i
soporte de miembros<
.4. Pase un cierre anónimo (lambda) como parámetro de comparación a las funciones de clasificación.
Donde C ++ 14 permite una expresión lambda aún más genérica:
que podría estar envuelto en una macro
haciendo que la creación de comparadores ordinarios sea bastante fluida:
fuente
bool X_less(X const &l, X const &r) const { return l.i < r.i; }
para el comparador, pero lasconst
palabras clave deben eliminarse (ya que no es una función miembro).std::sort
o similar, pero necesito una instancia deCompare
, por ejemplo, al crear una instancia de astd::set
?template<class T, class C> std::set<T, C> make_set(C const& compare) { return std::set<T, C>{ compare }; }
que podría usarse comoauto xset = make_set<X>([](auto && l, auto && r) { return l.i < r.i; });
.Estás en el camino correcto.
std::sort
utilizaráoperator<
como función de comparación por defecto. Entonces, para ordenar sus objetos, tendrá que sobrecargarbool operator<( const T&, const T& )
o proporcionar un functor que haga la comparación, de esta manera:La ventaja del uso de un functor es que puede usar una función con acceso a los miembros privados de la clase.
fuente
operator<
un miembro de la clase (o estructura), porque global podría usar miembros protegidos o privados. O deberías hacerlo amigo de la estructura C.Tenía curiosidad por saber si hay algún impacto medible en el rendimiento entre las diversas formas en que se puede llamar std :: sort, así que he creado esta prueba simple:
Lo que hace es crear un vector aleatorio, y luego mide cuánto tiempo se requiere para copiarlo y clasificar la copia (y calcular alguna suma de comprobación para evitar una eliminación de código muerto demasiado vigorosa).
Estaba compilando con g ++ (GCC) 7.2.1 20170829 (Red Hat 7.2.1-1)
Aquí están los resultados:
Parece que todas las opciones, excepto pasar el puntero de función, son muy similares, y pasar un puntero de función causa una penalización de + 30%.
También parece que la versión del operador <es ~ 1% más lenta (repetí la prueba varias veces y el efecto persiste), lo cual es un poco extraño, ya que sugiere que el código generado es diferente (me falta habilidad para analizar, guardar) temperatura de salida).
fuente
Sí,
std::sort()
con el tercer parámetro (función u objeto) sería más fácil. Un ejemplo: http://www.cplusplus.com/reference/algorithm/sort/fuente
En su clase, puede sobrecargar el operador "<".
fuente
Debajo está el código usando lambdas
fuente
fuente
Puede usar la clase de comparación definida por el usuario.
fuente
Para ordenar un vector, puede usar el algoritmo sort ().
El tercer parámetro usado puede ser mayor o menor o también se puede usar cualquier función u objeto. Sin embargo, el operador predeterminado es <si deja el tercer parámetro vacío.
fuente
si comparar es falso, hará "intercambio".
fuente