¿Cómo puedo ordenar dos vectores de la misma manera, con criterios que usan solo uno de los vectores?
Por ejemplo, supongamos que tengo dos vectores del mismo tamaño:
vector<MyObject> vectorA;
vector<int> vectorB;
Luego clasifico vectorA
usando alguna función de comparación. Esa clasificación reordenada vectorA
. ¿Cómo puedo aplicarme el mismo reordenamiento vectorB
?
Una opción es crear una estructura:
struct ExampleStruct {
MyObject mo;
int i;
};
y luego ordenar un vector que contiene el contenido de vectorA
y vectorB
comprimido en un solo vector:
// vectorC[i] is vectorA[i] and vectorB[i] combined
vector<ExampleStruct> vectorC;
Esta no parece una solución ideal. ¿Hay otras opciones, especialmente en C ++ 11?
vectorA
yvectorB
ambos por el contenido devectorB
.vector<pair<MyObject, int>>
. Entonces no tendría que preocuparse de que las dos listas no estén sincronizadas; un tipo reordena ambos conjuntos de datos simultáneamente. Y no hay estructura adicional que tener que escribir.Respuestas:
Encontrar una permutación de género
Dada una
std::vector<T>
y una comparación deT
's, queremos poder encontrar la permutación que usaría si tuviera que ordenar el vector usando esta comparación.template <typename T, typename Compare> std::vector<std::size_t> sort_permutation( const std::vector<T>& vec, Compare& compare) { std::vector<std::size_t> p(vec.size()); std::iota(p.begin(), p.end(), 0); std::sort(p.begin(), p.end(), [&](std::size_t i, std::size_t j){ return compare(vec[i], vec[j]); }); return p; }
Aplicar una permutación de ordenación
Dada una
std::vector<T>
y una permutación, queremos poder construir una nuevastd::vector<T>
que se reordena según la permutación.template <typename T> std::vector<T> apply_permutation( const std::vector<T>& vec, const std::vector<std::size_t>& p) { std::vector<T> sorted_vec(vec.size()); std::transform(p.begin(), p.end(), sorted_vec.begin(), [&](std::size_t i){ return vec[i]; }); return sorted_vec; }
Por supuesto, podría modificar
apply_permutation
para mutar el vector que le da en lugar de devolver una nueva copia ordenada. Este enfoque sigue siendo una complejidad de tiempo lineal y utiliza un bit por elemento en su vector. Teóricamente, sigue siendo una complejidad espacial lineal; pero, en la práctica, cuandosizeof(T)
es grande, la reducción en el uso de la memoria puede ser dramática. ( Ver detalles )template <typename T> void apply_permutation_in_place( std::vector<T>& vec, const std::vector<std::size_t>& p) { std::vector<bool> done(vec.size()); for (std::size_t i = 0; i < vec.size(); ++i) { if (done[i]) { continue; } done[i] = true; std::size_t prev_j = i; std::size_t j = p[i]; while (i != j) { std::swap(vec[prev_j], vec[j]); done[j] = true; prev_j = j; j = p[j]; } } }
Ejemplo
vector<MyObject> vectorA; vector<int> vectorB; auto p = sort_permutation(vectorA, [](T const& a, T const& b){ /*some comparison*/ }); vectorA = apply_permutation(vectorA, p); vectorB = apply_permutation(vectorB, p);
Recursos
std::vector
std::iota
std::sort
std::swap
std::transform
fuente
apply_permutation
para mutar el vector que le da en lugar de devolver una nueva copia ordenada " . Descubriría que desarrollar esto, al menos conceptualmente, es una adición útil a la respuesta. ¿Implementarías esto de la misma manera que élapply_permutation
mismo?Compare& compare
debe ser constante. De lo contrario, no puede usar std :: less <T> o similar.Con range-v3 , es simple, ordena una vista zip:
std::vector<MyObject> vectorA = /*..*/; std::vector<int> vectorB = /*..*/; ranges::v3::sort(ranges::view::zip(vectorA, vectorB));
o utilizar explícitamente la proyección:
ranges::v3::sort(ranges::view::zip(vectorA, vectorB), std::less<>{}, [](const auto& t) -> decltype(auto) { return std::get<0>(t); });
Manifestación
fuente
Me gustaría contribuir con una extensión que se me ocurrió. El objetivo es poder ordenar varios vectores al mismo tiempo utilizando una sintaxis simple.
El algoritmo es el mismo que propuso Timothy pero usando plantillas variadas , por lo que podemos ordenar múltiples vectores de tipos arbitrarios al mismo tiempo.
Aquí está el fragmento de código:
template <typename T, typename Compare> void getSortPermutation( std::vector<unsigned>& out, const std::vector<T>& v, Compare compare = std::less<T>()) { out.resize(v.size()); std::iota(out.begin(), out.end(), 0); std::sort(out.begin(), out.end(), [&](unsigned i, unsigned j){ return compare(v[i], v[j]); }); } template <typename T> void applyPermutation( const std::vector<unsigned>& order, std::vector<T>& t) { assert(order.size() == t.size()); std::vector<T> st(t.size()); for(unsigned i=0; i<t.size(); i++) { st[i] = t[order[i]]; } t = st; } template <typename T, typename... S> void applyPermutation( const std::vector<unsigned>& order, std::vector<T>& t, std::vector<S>&... s) { applyPermutation(order, t); applyPermutation(order, s...); } template<typename T, typename Compare, typename... SS> void sortVectors( const std::vector<T>& t, Compare comp, std::vector<SS>&... ss) { std::vector<unsigned> order; getSortPermutation(order, t, comp); applyPermutation(order, ss...); } // make less verbose for the usual ascending order template<typename T, typename... SS> void sortVectorsAscending( const std::vector<T>& t, std::vector<SS>&... ss) { sortVectors(t, std::less<T>(), ss...); }
Pruébelo en Ideone .
Explico esto un poco mejor en esta publicación de blog .
fuente
Clasificación in situ mediante permutación
Usaría una permutación como Timothy, aunque si sus datos son demasiado grandes y no desea asignar más memoria para el vector ordenado, debe hacerlo en el lugar . A continuación, se muestra un ejemplo de ordenación in situ O (n) (complejidad lineal) mediante permutación :
El truco consiste en obtener la permutación y la permutación inversa para saber dónde colocar los datos sobrescritos por el último paso de clasificación.
template <class K, class T> void sortByKey(K * keys, T * data, size_t size){ std::vector<size_t> p(size,0); std::vector<size_t> rp(size); std::vector<bool> sorted(size, false); size_t i = 0; // Sort std::iota(p.begin(), p.end(), 0); std::sort(p.begin(), p.end(), [&](size_t i, size_t j){ return keys[i] < keys[j]; }); // ----------- Apply permutation in-place ---------- // // Get reverse permutation item>position for (i = 0; i < size; ++i){ rp[p[i]] = i; } i = 0; K savedKey; T savedData; while ( i < size){ size_t pos = i; // Save This element; if ( ! sorted[pos] ){ savedKey = keys[p[pos]]; savedData = data[p[pos]]; } while ( ! sorted[pos] ){ // Hold item to be replaced K heldKey = keys[pos]; T heldData = data[pos]; // Save where it should go size_t heldPos = rp[pos]; // Replace keys[pos] = savedKey; data[pos] = savedData; // Get last item to be the pivot savedKey = heldKey; savedData = heldData; // Mark this item as sorted sorted[pos] = true; // Go to the held item proper location pos = heldPos; } ++i; } }
fuente
Haz un vector de pares con tus vectores individuales.
inicializar vector de pares
Sumando a un vector de par
Hacer un comparador de ordenación personalizado:
ordenar un vector de objetos personalizados
http://rosettacode.org/wiki/Sort_using_a_custom_comparator#C.2B.2B
Ordena tu vector de pares.
Separe su vector de pares en vectores individuales.
Pon todo esto en una función.
Código:
std::vector<MyObject> vectorA; std::vector<int> vectorB; struct less_than_int { inline bool operator() (const std::pair<MyObject,int>& a, const std::pair<MyObject,int>& b) { return (a.second < b.second); } }; sortVecPair(vectorA, vectorB, less_than_int()); // make sure vectorA and vectorB are of the same size, before calling function template <typename T, typename R, typename Compare> sortVecPair(std::vector<T>& vecA, std::vector<R>& vecB, Compare cmp) { std::vector<pair<T,R>> vecC; vecC.reserve(vecA.size()); for(int i=0; i<vecA.size(); i++) { vecC.push_back(std::make_pair(vecA[i],vecB[i]); } std::sort(vecC.begin(), vecC.end(), cmp); vecA.clear(); vecB.clear(); vecA.reserve(vecC.size()); vecB.reserve(vecC.size()); for(int i=0; i<vecC.size(); i++) { vecA.push_back(vecC[i].first); vecB.push_back(vecC[i].second); } }
fuente
Supongo que vectorA y vectorB tienen longitudes iguales. Podría crear otro vector, llamémoslo pos, donde:
pos[i] = the position of vectorA[i] after sorting phase
y luego, puede ordenar vectorB usando pos, es decir, crear vectorBsorted donde:
y luego vectorBsorted se ordena mediante la misma permutación de índices que vectorA.
fuente
No estoy seguro de si esto funciona, pero usaría algo como esto. Por ejemplo, para clasificar dos vectores, usaría el método de clasificación de burbujas descendentes y pares de vectores.
Para la clasificación de burbujas descendente, crearía una función que requiera un par de vectores.
void bubbleSort(vector< pair<MyObject,int> >& a) { bool swapp = true; while (swapp) { int key; MyObject temp_obj; swapp = false; for (size_t i = 0; i < a.size() - 1; i++) { if (a[i].first < a[i + 1].first) { temp_obj = a[i].first; key = a[i].second; a[i].first = a[i + 1].first; a[i + 1].first = temp_obj; a[i].second = a[i + 1].second; a[i + 1].second = key; swapp = true; } } } }
Después de eso, pondría sus 2 valores vectoriales en un par de vectores. Si puede agregar valores al mismo tiempo, use este y luego llame a la función de clasificación de burbujas.
vector< pair<MyObject,int> > my_vector; my_vector.push_back( pair<MyObject,int> (object_value,int_value)); bubbleSort(my_vector);
Si desea usar valores después de sumar a sus 2 vectores, puede usar este y luego llamar a la función de clasificación de burbujas.
vector< pair<MyObject,int> > temp_vector; for (size_t i = 0; i < vectorA.size(); i++) { temp_vector.push_back(pair<MyObject,int> (vectorA[i],vectorB[i])); } bubbleSort(temp_vector);
Espero que esto ayude. Saludos, Caner
fuente
Recientemente escribí un iterador zip adecuado que funciona con los algoritmos stl. Te permite producir código como este:
std::vector<int> a{3,1,4,2}; std::vector<std::string> b{"Alice","Bob","Charles","David"}; auto zip = Zip(a,b); std::sort(zip.begin(), zip.end()); for (const auto & z: zip) std::cout << z << std::endl;
Está contenido en un solo encabezado y el único requisito es C ++ 17. Compruébalo en GitHub .
También hay una publicación sobre codereview que contiene todo el código fuente.
fuente
Basado en la respuesta de Timothy Shields.
Con un pequeño ajuste
apply_permutaion
, puede aplicar la permutación a múltiples vectores de diferentes tipos a la vez con el uso de una expresión de pliegue.template <typename T, typename... Ts> void apply_permutation(const std::vector<size_t>& perm, std::vector<T>& v, std::vector<Ts>&... vs) { std::vector<bool> done(v.size()); for(size_t i = 0; i < v.size(); ++i) { if(done[i]) continue; done[i] = true; size_t prev = i; size_t curr = perm[i]; while(i != curr) { std::swap(v[prev], v[curr]); (std::swap(vs[prev], vs[curr]), ...); done[curr] = true; prev = curr; curr = perm[curr]; } } }
fuente