web-dev-qa-db-fra.com

Quel est le meilleur moyen de concaténer deux vecteurs?

J'utilise la multipression et je souhaite fusionner les résultats. Par exemple:

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

Je veux que AB ait le contenu de A et le contenu de B dans cet ordre. Quel est le moyen le plus efficace de faire quelque chose comme ça?

167
jmasterx
AB.reserve( A.size() + B.size() ); // preallocate memory
AB.insert( AB.end(), A.begin(), A.end() );
AB.insert( AB.end(), B.begin(), B.end() );
284

C’est précisément ce à quoi sert la fonction membre std::vector::insert

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

Cela dépend si vous avez vraiment besoin de concaténer physiquement les deux vecteurs ou si vous voulez donner l’apparence d’une concaténation dans l’intérêt de l’itération. Le boost :: rejoindre la fonction

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

vais vous donner ceci.

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

devrait vous donner

1
2
3
4
5
6

Note boost :: join ne copie pas les deux vecteurs dans un nouveau conteneur, mais génère une paire d'itérateurs (plage) couvrant l'étendue des deux conteneurs. Il y aura une surcharge de performances mais peut-être moins que de copier toutes les données dans un nouveau conteneur en premier.

24
bradgonesurfing

Basé sur Kiril V. Lyadvinsky answer , j'ai créé une nouvelle version. Cet extrait utilise un modèle et une surcharge. Avec cela, vous pouvez écrire vector3 = vector1 + vector2 et vector4 += vector3. J'espère que ça peut aider.

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
}
8
aloisdg

Encore une variante simple qui n’a pas encore été mentionnée:

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

Et en utilisant l'algorithme de fusion:

#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;
}
3
D. Alex

Dans le sens de la réponse de Bradgonesurfing, il arrive souvent que l'on ne oblige) pas = (obligatoirement concaténer deux vecteurs, mais simplement travailler avec eux comme s'ils étaient concaténés. Cela semble être votre cas et vous pouvez le faire sans utiliser les bibliothèques Boost.

L'astuce consiste à créer un proxy vectoriel: une classe wrapper manipulant références pour les deux vecteurs, vus de l'extérieur comme un seul, contigus, qui peuvent ensuite être consultés/parcourus exactement comme vous le feriez sur un réel. vecteur.

USAGE

std::vector<int> A{ 1, 2, 3, 4, 5};
std::vector<int> B{ 10, 20, 30 };

VecProxy<int> AB(A, B);  // ----> O(1)

for (size_t i = 0; i < AB.size(); i++)
    std::cout << AB[i] << " ";  // ----> Output: 1 2 3 4 5 10 20 30

std::cout << AB[6]; // ----> Output: 20

IMPLÉMENTATION

template <class T>
class VecProxy {
private:
    std::vector<T>& v1;
    std::vector<T>& v2;
public:
    VecProxy(std::vector<T>& ref1, std::vector<T>& ref2) : v1(ref1), v2(ref2) {}
    T& operator[](const size_t& i);
    const T& operator[](const size_t& i) const;
    const size_t size() const;
};
template<class T>
T& VecProxy<T>::operator[](const size_t& i){
    return (i < v1.size()) ? v1[i] : v2[i - v1.size()];
};
template <class T>
const T& VecProxy<T>::operator[](const size_t& i) const{
    return (i < v1.size()) ? v1[i] : v2[i - v1.size()];
};
template <class T>
const size_t VecProxy<T>::size() const { return v1.size() + v2.size(); };

AVANTAGE PRINCIPAL

C'est O(1) (temps constant) pour le créer, avec un minimum d'allocation de mémoire supplémentaire. En pratique, c’est une opération rapide, même en considérant des vecteurs énormes, puisque vous remplacez | B | (ou | A | + | B |) élément copie par zéro. En outre, il offre exactement le comportement souhaité.

La concaténation des vecteurs est au moins égale à O (| B |) (lorsque B est ajouté à A), quelle que soit la technique utilisée. Dans votre cas, puisque vous avez l’intention de travailler avec un 3ème vecteur, AB, c’est O (| A | + | B |). En fonction à la fois de la taille des vecteurs et du nombre d'opérations de concaténation nécessaires, cela peut constituer un goulot d'étranglement. L'astuce ci-dessus y fait face.

QUELQUES PRODUITS À CONSIDÉRER

  • Vous ne devriez y aller que si vous savez vraiment ce que vous faites quand vous avez affaire à des références. Cette solution est destinée au but spécifique de la question posée, pour laquelle cela fonctionne assez bien. Son utilisation dans tout autre contexte peut entraîner un comportement inattendu si vous n'êtes pas sûr du fonctionnement des références.
  • Dans cet exemple, AB fournit non seulement un accès const mais non-const. N'hésitez pas à l'enlever. Étant donné que AB contient des références, l'attribution de valeurs affectera également les éléments d'origine dans A et/ou B. Qu'il s'agisse ou non d'une fonctionnalité souhaitable, il s'agit d'une question spécifique à l'application qu'il convient d'examiner attentivement.
  • De même, toute modification apportée à A ou à B (comme l'attribution de valeurs, le redimensionnement, etc.) "modifiera" également AB. Ce n'est pas nécessairement mauvais (en fait, cela peut être très pratique: AB n'a jamais besoin d'être explicitement mis à jour pour rester synchronisé sur A et B), mais c'est certainement un comportement dont il faut être conscient.
  • Comme chaque accès à un élément est précédé d'un test (à savoir, "i <v1.size ()"), le temps d'accès VecProxy, bien que constant, est également plus lent que celui des vecteurs. Cela peut donc ne pas être la bonne solution si vous devez, par exemple, effectuer des millions d’opérations d’accès par seconde, c’est-à-dire si le goulot d’étranglement réside dans les opérations d’accès.
  • Cette approche peut être généralisée à n vecteurs. Je n'ai pas essayé, mais ça ne devrait pas être un gros problème.
  • Cela n'a aucun sens (à mes yeux) de fournir des constructeurs de copie à de tels mandataires (car ce ne sont pas des vecteurs après tout).
  • Il n'est pas possible (ou du moins trop facile) de globalement trier un VecProxy, car tous les éléments ne se rapportent pas au même conteneur.
2
Ronald Chiesse

Si vos vecteurs sont triés *, consultez set_union dans <algorithm>.

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

Il y a un exemple plus complet dans le lien

* merci rlbond

0
Cogwheel

Toutes les solutions sont correctes, mais j’ai trouvé plus facile d’écrire une fonction pour implémenter ceci. comme ça:

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

De cette façon, vous pouvez éviter le placement temporaire comme ceci:

ContainerInsert(vec, GetSomeVector());
0
user3360767