Necesito tomar un vector C ++ con potencialmente muchos elementos, borrar duplicados y ordenarlo.
Actualmente tengo el siguiente código, pero no funciona.
vec.erase(
std::unique(vec.begin(), vec.end()),
vec.end());
std::sort(vec.begin(), vec.end());
¿Cómo puedo hacer esto correctamente?
Además, ¿es más rápido borrar los duplicados primero (similar al codificado anteriormente) o realizar primero la clasificación? Si realizo la ordenación primero, ¿se garantiza que permanecerá ordenada después?std::unique
se ejecute?
¿O hay otra forma (quizás más eficiente) de hacer todo esto?
Respuestas:
Estoy de acuerdo con R. Pate y Todd Gardner ; una
std::set
podría ser una buena idea aquí. Incluso si está atascado usando vectores, si tiene suficientes duplicados, es mejor que cree un conjunto para hacer el trabajo sucio.Comparemos tres enfoques:
Solo usando vector, sort + unique
Convertir a conjunto (manualmente)
Convertir a conjunto (usando un constructor)
Así es como funcionan estos a medida que se modifican los duplicados:
Resumen : cuando el número de duplicados es lo suficientemente grande, en realidad es más rápido convertirlo a un conjunto y luego volcar los datos nuevamente en un vector .
Y por alguna razón, hacer la conversión del conjunto manualmente parece ser más rápido que usar el constructor del conjunto, al menos en los datos aleatorios del juguete que utilicé.
fuente
Rehice el perfil de Nate Kohl y obtuve resultados diferentes. Para mi caso de prueba, ordenar directamente el vector siempre es más eficiente que usar un conjunto. Agregué un nuevo método más eficiente, usando un
unordered_set
.Tenga en cuenta que el
unordered_set
método solo funciona si tiene una buena función hash para el tipo que necesita unificado y ordenado. ¡Para los int, esto es fácil! (La biblioteca estándar proporciona un hash predeterminado que es simplemente la función de identidad). Además, no olvide ordenar al final ya que unordered_set es, bueno, no ordenado :)Hice algo de investigación dentro de la
set
eunordered_set
implementación y descubrí que el constructor en realidad la construcción de un nuevo nodo para cada elemento, antes de comprobar su valor para determinar si en realidad se debe insertar (en aplicación de Visual Studio, por lo menos).Aquí están los 5 métodos:
f1: solo usando
vector
,sort
+unique
f2: Convertir a
set
(usando un constructor)f3: Convertir a
set
(manualmente)f4: Convertir a
unordered_set
(usando un constructor)f5: Convertir a
unordered_set
(manualmente)Hice la prueba con un vector de 100,000,000 ints elegidos al azar en rangos [1,10], [1,1000] y [1,100000]
Los resultados (en segundos, más pequeño es mejor):
fuente
sort
unique
#include <algorithm>
CWUK
escenario que tiene una naturaleza de propaganda para frenar elemplace
tipo de construcción.std::unique
solo elimina elementos duplicados si son vecinos: primero debe ordenar el vector antes de que funcione como lo desea.std::unique
está definido para ser estable, por lo que el vector aún se ordenará después de ejecutarse en él.fuente
No estoy seguro de para qué estás usando esto, así que no puedo decir esto con 100% de certeza, pero normalmente cuando pienso en un contenedor "ordenado, único", pienso en un std :: set . Podría ser mejor para su caso de uso:
De lo contrario, la ordenación previa a la llamada única (como señalaron las otras respuestas) es el camino a seguir.
fuente
std::unique
solo funciona en ejecuciones consecutivas de elementos duplicados, por lo que es mejor ordenar primero. Sin embargo, es estable, por lo que su vector permanecerá ordenado.fuente
Aquí hay una plantilla para hacerlo por usted:
llámalo como:
fuente
erase()
método, de lo contrario, debe devolver el nuevo iterador final y hacer que el código de llamada trunca el contenedor.La eficiencia es un concepto complicado. Hay consideraciones de tiempo frente a espacio, así como mediciones generales (donde solo se obtienen respuestas vagas como O (n)) frente a respuestas específicas (por ejemplo, la clasificación de burbujas puede ser mucho más rápida que la clasificación rápida, dependiendo de las características de entrada).
Si tiene relativamente pocos duplicados, entonces la ordenación seguida de única y borrar parece el camino a seguir. Si tuviera relativamente muchos duplicados, crear un conjunto a partir del vector y dejarlo hacer el trabajo pesado podría vencerlo fácilmente.
No se concentre solo en la eficiencia del tiempo tampoco. Sort + unique + erase opera en el espacio O (1), mientras que la construcción del set opera en el espacio O (n). Y ninguno se presta directamente a una paralelización de reducción de mapas (para conjuntos de datos realmente enormes ).
fuente
Debe ordenarlo antes de llamar
unique
porqueunique
solo elimina los duplicados que están uno al lado del otro.editar: 38 segundos ...
fuente
unique
solo elimina elementos duplicados consecutivos (lo cual es necesario para que se ejecute en tiempo lineal), por lo que primero debe realizar la ordenación. Seguirá ordenado después de la llamada aunique
.fuente
Si no desea cambiar el orden de los elementos, puede probar esta solución:
fuente
Suponiendo que a es un vector, elimine los duplicados contiguos usando
a.erase(unique(a.begin(),a.end()),a.end());
se ejecuta en tiempo O (n) .fuente
std::sort
primero.Como ya se indicó,
unique
requiere un contenedor ordenado. Además, enunique
realidad no elimina elementos del contenedor. En cambio, se copian hasta el final,unique
devuelve un iterador que apunta al primer elemento duplicado y se espera que llameerase
para eliminar realmente los elementos.fuente
El enfoque estándar sugerido por Nate Kohl, simplemente usando vector, sort + unique:
no funciona para un vector de punteros.
Mire cuidadosamente este ejemplo en cplusplus.com .
En su ejemplo, los "llamados duplicados" movidos al final se muestran realmente como? (valores indefinidos), porque esos "llamados duplicados" son A VECES "elementos extra" y A VECES hay "elementos faltantes" que estaban en el vector original.
Se produce un problema cuando se usa
std::unique()
en un vector de punteros a objetos (pérdidas de memoria, mala lectura de datos de HEAP, liberaciones duplicadas, que causan fallas de segmentación, etc.).Aquí está mi solución al problema: reemplazar
std::unique()
conptgi::unique()
.Vea el archivo ptgi_unique.hpp a continuación:
Y aquí está el programa UNIT Test que utilicé para probarlo:
fuente
std::unique
tener [1, 2, 3, 2] no puede llamar a eliminar en 2 ya que eso dejaría un puntero colgante a 2! => ¡Simplemente no llame a eliminar en los elementos entrenewEnd = std::unique
ystd::end
ya que todavía tiene punteros a estos elementos[std::begin, newEnd)
!unique
a unvector<unique_ptr<T>>
, ya que el único valor duplicado que puede contener ese vector esnullptr
.Con la biblioteca Ranges (que viene en C ++ 20) simplemente puede usar
Tenga en cuenta que en realidad elimina los elementos duplicados, no solo los mueve.
fuente
Sobre los puntos de referencia alexK7. Los probé y obtuve resultados similares, pero cuando el rango de valores es de 1 millón, los casos que usan std :: sort (f1) y std :: unordered_set (f5) producen un tiempo similar. Cuando el rango de valores es 10 millones, f1 es más rápido que f5.
Si el rango de valores es limitado y los valores no están firmados int, es posible usar std :: vector, cuyo tamaño corresponde al rango dado. Aquí está el código:
fuente
sort (v.begin (), v.end ()), v.erase (unique (v.begin (), v, end ()), v.end ());
fuente
Si está buscando rendimiento y uso
std::vector
, le recomiendo el que proporciona este enlace de documentación .fuente
fuente
Si no desea modificar el vector (borrar, ordenar), puede usar la biblioteca Newton . En la sublibrary del algoritmo hay una llamada a la función, copy_single
así que puedes:
donde copy es el vector en el que desea hacer retroceder la copia de los elementos únicos. pero recuerda que empujas los elementos hacia atrás y no creas un nuevo vector
de todos modos, esto es más rápido porque no borras () los elementos (lo que lleva mucho tiempo, excepto cuando pop_back (), debido a la reasignación)
Hago algunos experimentos y es más rápido.
Además, puedes usar:
A veces es aún más rápido.
fuente
unique_copy
.Código más comprensible de: https://en.cppreference.com/w/cpp/algorithm/unique
salida:
fuente
fuente
Este es el ejemplo del problema de eliminación de duplicados que ocurre con std :: unique (). En una máquina LINUX, el programa se bloquea. Lea los comentarios para más detalles.
fuente
vector
contiene enteros, no punteros, y no especifica un comparador).Esta es una función que creé que puedes usar para eliminar repeticiones. Los archivos de encabezado necesarios son just
<iostream>
y<vector>
.fuente