Каков наилучший способ конкатенировать два вектора?

Я использую multitreading и хочу объединить результаты. Например:

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

Я хочу, чтобы AB должен был содержать содержимое A и содержимое B в этом порядке. Какой самый эффективный способ сделать что-то подобное?

Ответ 1

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

Ответ 2

Именно это функция-член std::vector::insert для

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

Ответ 3

Зависит от того, действительно ли вам нужно физически объединить два вектора или вы хотите создать конкатенацию ради итерации. Функция boost:: join

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

предоставит вам это.

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

должен предоставить вам

1
2
3
4
5
6

Примечание boost:: join не копирует два вектора в новый контейнер но генерирует пару итераторов (диапазон), которые охватывают диапазон оба контейнера. Будет накладные расходы на производительность, но, возможно, меньше того, что сначала копирует все данные в новый контейнер.

Ответ 4

На основе Ответ Кирилла В. Лядвинского, я сделал новую версию. Этот фрагмент использует шаблон и перегружает. С его помощью вы можете написать vector3 = vector1 + vector2 и vector4 += vector3. Надеюсь, это поможет.

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
}

Ответ 5

Еще один простой вариант, который еще не был упомянут:

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

И используя алгоритм слияния:

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

Ответ 6

В направлении ответа Bradgonesurfing, часто не нужно need объединять два вектора (O (n)), а вместо этого просто работать с ними, как если бы они были сцеплены (O (1) ). Если это ваш случай, это можно сделать без использования библиотек Boost.

Хитрость заключается в создании векторного прокси: класс-обертка, который манипулирует ссылками на оба вектора, внешне воспринимается как один непрерывный, к которому затем можно получить доступ/пройти точно так же, как вы это делаете на реальном векторе,

ПРИМЕНЕНИЕ

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

VecProxy<int> AB(A, B);  // ----> O(1). No copies performed!

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

РЕАЛИЗАЦИЯ

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

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

ОСНОВНАЯ ВЫГОДА

Это O (1) (постоянное время) для его создания и с минимальным выделением дополнительной памяти. На практике это быстрая операция даже при рассмотрении огромных векторов, поскольку вы заменяете | B | (или | A | + | B |) копирует элемент на ноль. Кроме того, он обеспечивает именно желаемое поведение.

Конкатенация векторов составляет, по меньшей мере, O (| B |) (когда B добавляется к A), независимо от используемого метода. В вашем случае, поскольку вы намереваетесь работать с третьим вектором, AB, это O (| A | + | B |). В зависимости от размера векторов и количества необходимых операций конкатенации, это может быть узким местом. Вышеприведенный трюк решает эту проблему.

НЕКОТОРЫЕ ВЕЩИ ДЛЯ РАССМОТРЕНИЯ

  • Вы должны делать это только в том случае, если вы действительно знаете, что делаете при работе со ссылками. Это решение предназначено для конкретной цели поставленного вопроса, для чего оно работает довольно хорошо. Использование его в любом другом контексте может привести к неожиданному поведению, если вы не уверены в том, как работают ссылки.
  • В этом примере AB не предоставляет неконстантный оператор доступа ([]). Не стесняйтесь включать его, но имейте в виду: поскольку AB содержит ссылки, чтобы назначить его значения также влияют на исходные элементы в пределах A и/или B. Независимо от того, является ли это желательная особенность, это вопрос конкретного приложения, нужно внимательно обдумайте.
  • Любые изменения, сделанные непосредственно в A или B (например, присвоение значений, изменение размера и т.д.) также будет "модифицировать" AB. Это не обязательно плохо (на самом деле, это может быть очень удобно: AB никогда не нужно явно обновленный, чтобы держать себя синхронизированным и к A и к B), но это безусловно, поведение, которое нужно знать.
  • Потому что каждому доступу к элементу предшествует тест (а именно & Lt; v1.size() "), время доступа VecProxy, хотя и постоянное, также медленнее, чем у векторов. Таким образом, возможно, это не тот путь, если скажем, вам нужно выполнять миллионы операций доступа в секунду, то есть, если узкое место лежит в операциях доступа.
  • Этот подход можно обобщить на n векторов. Я не пробовал, но это не должно быть большим делом.
  • Не имеет смысла (на мой взгляд) предоставлять конструкторы копирования такие прокси (поскольку они не являются векторами в конце концов).
  • Невозможно (или, по крайней мере, слишком легко) глобально отсортировать VecProxy, так как не все элементы принадлежат одному и тому же контейнеру.

Ответ 7

Если ваши векторы отсортированы *, просмотрите set_union из <algorithm> .

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

Вот более подробный пример в ссылке

* thanks rlbond

Ответ 8

Все решения верны, но мне было проще написать функцию для ее реализации. например:

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

Таким образом вы можете избежать временного размещения следующим образом:

ContainerInsert(vec, GetSomeVector());