2010-07-05 35 views
129

Estoy usando multitreading y quiero unir los resultados. Por ejemplo:¿Cuál es la mejor manera de concatenar dos vectores?

std::vector<int> A; 
std::vector<int> B; 
std::vector<int> AB; 

Quiero que AB tenga el contenido de A y el contenido de B en ese orden. ¿Cuál es la forma más eficiente de hacer algo como esto?

Respuesta

226
AB.reserve(A.size() + B.size()); // preallocate memory 
AB.insert(AB.end(), A.begin(), A.end()); 
AB.insert(AB.end(), B.begin(), B.end()); 
+3

¡Gracias! No habría pensado en reserva. – jmasterx

+0

¿Cuál es la complejidad de tiempo para una operación de este tipo? ¿Es el tiempo constante o O (n)? –

+4

debe copiar cada elemento, por lo que es O (n) –

0

Si sus vectores están ordenados *, consulte set_union del <algoritmo>.

set_union(A.begin(), A.end(), B.begin(), B.end(), AB.begin()); 

Hay un ejemplo más a fondo en el enlace

* gracias rlbond

+2

'set_union' solo funciona en rangos ordenados. – rlbond

+3

Además, no hace lo mismo que una anexión directa: los elementos en el rango de salida son únicos, lo que puede no ser lo que el OP quería (incluso podrían no ser comparables).Ciertamente no es la forma más eficiente de hacerlo. – Peter

36

Esto es precisamente lo que la función miembro std::vector::insert es para

std::vector<int> AB = A; 
AB.insert(AB.end(), B.begin(), B.end()); 
+0

Excepto que 'std :: vector :: insert' es muy lento cuando todo lo que intenta hacer es concatenar dos vectores. Aquí es donde '' falla y realmente desea que 'std :: vector :: extend' sea un método real. –

+4

@Nick: ¿Lento comparado con qué? – GManNickG

+2

¿Tal vez compruebe que haya suficiente espacio en cada inserción de elemento? Usar la reserva de antemano lo acelerará. – RvdK

20

depende de si se realmente necesita concatenar físicamente los dos vectores o desea dar la apariencia de concatenación del bien de la iteración. El impulso :: unirse función

http://www.boost.org/doc/libs/1_43_0/libs/range/doc/html/range/reference/utilities/join.html

te dará esto.

std::vector<int> v0; 
v0.push_back(1); 
v0.push_back(2); 
v0.push_back(3); 

std::vector<int> v1; 
v1.push_back(4); 
v1.push_back(5); 
v1.push_back(6); 
... 

BOOST_FOREACH(const int & i, boost::join(v0, v1)){ 
    cout << i << endl; 
} 

debe darle

1 
2 
3 
4 
5 
6 

Nota impulso :: unirse no copia los dos vectores en un nuevo contenedor pero genera un par de iteradores (rango) que cubren el lapso de ambos contenedores . Habrá una sobrecarga de rendimiento, pero tal vez menos que copiar primero todos los datos a un nuevo contenedor.

+4

+1 para aumentar :: unir –

8

Basado en Kiril V. Lyadvinsky answer, hice una nueva versión. Este fragmento usa plantilla y sobrecarga. Con él, puede escribir vector3 = vector1 + vector2 y vector4 += vector3. Espero que pueda ayudar.

template <typename T> 
std::vector<T> operator+(const std::vector<T> &A, const std::vector<T> &B) 
{ 
    std::vector<T> AB; 
    AB.reserve(A.size() + B.size());    // preallocate memory 
    AB.insert(AB.end(), A.begin(), A.end());  // add A; 
    AB.insert(AB.end(), B.begin(), B.end());  // add B; 
    return AB; 
} 

template <typename T> 
std::vector<T> &operator+=(std::vector<T> &A, const std::vector<T> &B) 
{ 
    A.reserve(A.size() + B.size());    // preallocate memory without erase original data 
    A.insert(A.end(), B.begin(), B.end());   // add B; 
    return A;          // here A could be named AB 
} 
+1

¿Quiere agregar los elementos de cada vector entre sí? ¿O quieres agregar? Esto está claro ahora, pero durante los próximos 5 años ..? No debe sobrecargar al operador si el significado es ambiguo. –

+1

@ S.R Quiero concatenarte. Escribí esta respuesta hace 3 años. Aún sé lo que significa. No hay problema allí. Si C++ pudiera proporcionar su propia sobrecarga, será aún mejor.(y sí '::' está tomado;) – aloisdg

0

Todas las soluciones son correctas, pero me pareció más fácil simplemente escribir una función para implementar esto. de esta manera:

template <class T1, class T2> 
void ContainerInsert(T1 t1, T2 t2) 
{ 
    t1->insert(t1->end(), t2->begin(), t2->end()); 
} 

De esta manera se puede evitar la colocación temporal de esta manera:

ContainerInsert(vec, GetSomeVector()); 
+1

Es una pena que esté mal. Esas copias! –

2

una variante más simple que se menciona no chorro:

copy(A.begin(),A.end(),std::back_inserter(AB)); 
copy(B.begin(),B.end(),std::back_inserter(AB)); 

y el uso de fusionar algoritmo:

#include <algorithm> #include <vector> #include <iterator> #include <iostream> #include <sstream> #include <string> template<template<typename, typename...> class Container, class T> std::string toString(const Container<T>& v) { std::stringstream ss; std::copy(v.begin(), v.end(), std::ostream_iterator<T>(ss, "")); return ss.str(); }; int main() { std::vector<int> A(10); std::vector<int> B(5); //zero filled std::vector<int> AB(15); std::for_each(A.begin(), A.end(), [](int& f)->void { f = rand() % 100; }); std::cout << "before merge: " << toString(A) << "\n"; std::cout << "before merge: " << toString(B) << "\n"; merge(B.begin(),B.end(), begin(A), end(A), AB.begin(), [](int&,int&)->bool {}); std::cout << "after merge: " << toString(AB) << "\n"; return 1; }

Cuestiones relacionadas