Jak połączyć dwa std::vector
s?
a + b
lub a.concat(b)
w standardowej bibliotece? Być może domyślna implementacja byłaby nieoptymalna, ale każda konkatenacja macierzy nie musi być mikrooptymalizowana
Jak połączyć dwa std::vector
s?
a + b
lub a.concat(b)
w standardowej bibliotece? Być może domyślna implementacja byłaby nieoptymalna, ale każda konkatenacja macierzy nie musi być mikrooptymalizowana
Odpowiedzi:
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
reserve
najpierw wywołanie wektora docelowego?
vector1.capacity() >= 2 * vector1.size()
. Co jest nietypowe, chyba że zadzwoniłeś std::vector::reserve()
. W przeciwnym razie wektor przeniesie się ponownie, unieważniając iteratory przekazane jako parametry 2 i 3.
.concat
lub +=
coś w tym stylu
Jeśli używasz C ++ 11 i chcesz przenieść elementy, a nie tylko je kopiować, możesz użyć std::move_iterator
wraz z wstawką (lub kopią):
#include <vector>
#include <iostream>
#include <iterator>
int main(int argc, char** argv) {
std::vector<int> dest{1,2,3,4,5};
std::vector<int> src{6,7,8,9,10};
// Move elements from src to dest.
// src is left in undefined but safe-to-destruct state.
dest.insert(
dest.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end())
);
// Print out concatenated vector.
std::copy(
dest.begin(),
dest.end(),
std::ostream_iterator<int>(std::cout, "\n")
);
return 0;
}
Nie będzie to bardziej wydajne w przykładzie z ints, ponieważ przenoszenie ich nie jest bardziej wydajne niż ich kopiowanie, ale w przypadku struktury danych ze zoptymalizowanymi ruchami można uniknąć kopiowania niepotrzebnego stanu:
#include <vector>
#include <iostream>
#include <iterator>
int main(int argc, char** argv) {
std::vector<std::vector<int>> dest{{1,2,3,4,5}, {3,4}};
std::vector<std::vector<int>> src{{6,7,8,9,10}};
// Move elements from src to dest.
// src is left in undefined but safe-to-destruct state.
dest.insert(
dest.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end())
);
return 0;
}
Po przeniesieniu element src pozostaje w nieokreślonym, ale bezpiecznym stanie do zniszczenia, a jego poprzednie elementy zostały przeniesione bezpośrednio do nowego elementu dest na końcu.
std::move(src.begin(), src.end(), back_inserter(dest))
?
Chciałbym użyć funkcji wstawiania , na przykład:
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Lub możesz użyć:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
Ten wzór jest przydatny, jeśli dwa wektory nie zawierają dokładnie tego samego rodzaju rzeczy, ponieważ można użyć czegoś zamiast std :: back_inserter do konwersji z jednego typu na drugi.
reserve
najpierw zadzwonić . Przyczyna std::copy
jest czasami przydatna, jeśli chcesz użyć czegoś innego niż back_inserter
.
W C ++ 11 wolałbym, aby dołączyć wektor b do a:
std::move(b.begin(), b.end(), std::back_inserter(a));
kiedy a
i b
nie pokrywają się i b
nie będą już używane.
To jest std::move
z <algorithm>
, nie zwykle std::move
z <utility>
.
insert
bezpieczniejszej drogi.
insert()
ze move_iterator
s? Jeśli tak to jak?
std::move
tu mówimy, ponieważ większość ludzi nie zna tego przeciążenia. Mam nadzieję, że to poprawa.
std::vector<int> first;
std::vector<int> second;
first.insert(first.end(), second.begin(), second.end());
Wolę taki, który jest już wspomniany:
a.insert(a.end(), b.begin(), b.end());
Ale jeśli używasz C ++ 11, istnieje jeszcze jeden ogólny sposób:
a.insert(std::end(a), std::begin(b), std::end(b));
Również nie stanowi części pytania, ale zaleca się użycie reserve
przed dołączeniem w celu uzyskania lepszej wydajności. A jeśli konkatenujesz wektor ze sobą, bez rezerwowania go zawodzi, więc zawsze powinieneś reserve
.
Więc w zasadzie to, czego potrzebujesz:
template <typename T>
void Append(std::vector<T>& a, const std::vector<T>& b)
{
a.reserve(a.size() + b.size());
a.insert(a.end(), b.begin(), b.end());
}
std::
jeśli typ a
pochodzi od std
, co eliminuje aspekt ogólny.
Powinieneś użyć vector :: insert
v1.insert(v1.end(), v2.begin(), v2.end());
Ogólny wzrost wydajności dla Złącz jest sprawdzenie rozmiaru wektorów. I scal / wstaw mniejszy z większym.
//vector<int> v1,v2;
if(v1.size()>v2.size()) {
v1.insert(v1.end(),v2.begin(),v2.end());
} else {
v2.insert(v2.end(),v1.begin(),v1.end());
}
v1.insert(v2.end()...
używa iteratora v2
do określenia pozycji w v1
.
Jeśli chcesz mieć możliwość zwięzłego łączenia wektorów, możesz przeciążyć +=
operatora.
template <typename T>
std::vector<T>& operator +=(std::vector<T>& vector1, const std::vector<T>& vector2) {
vector1.insert(vector1.end(), vector2.begin(), vector2.end());
return vector1;
}
Następnie możesz to tak nazwać:
vector1 += vector2;
Jeśli interesuje Cię silna gwarancja wyjątku (gdy konstruktor kopii może zgłosić wyjątek):
template<typename T>
inline void append_copy(std::vector<T>& v1, const std::vector<T>& v2)
{
const auto orig_v1_size = v1.size();
v1.reserve(orig_v1_size + v2.size());
try
{
v1.insert(v1.end(), v2.begin(), v2.end());
}
catch(...)
{
v1.erase(v1.begin() + orig_v1_size, v1.end());
throw;
}
}
Podobne append_move
z silną gwarancją nie może być ogólnie zaimplementowane, jeśli konstruktor ruchu elementu wektorowego może rzucać (co jest mało prawdopodobne, ale nadal).
v1.erase(...
też rzucać?
insert
już to obsługuje. Również to wywołanie erase
jest równoważne z resize
.
Dodaj ten do pliku nagłówka:
template <typename T> vector<T> concat(vector<T> &a, vector<T> &b) {
vector<T> ret = vector<T>();
copy(a.begin(), a.end(), back_inserter(ret));
copy(b.begin(), b.end(), back_inserter(ret));
return ret;
}
i użyj tego w ten sposób:
vector<int> a = vector<int>();
vector<int> b = vector<int>();
a.push_back(1);
a.push_back(2);
b.push_back(62);
vector<int> r = concat(a, b);
r będzie zawierać [1,2,62]
Oto rozwiązanie ogólnego zastosowania wykorzystujące semantykę przenoszenia C ++ 11:
template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
if (lhs.empty()) return rhs;
if (rhs.empty()) return lhs;
std::vector<T> result {};
result.reserve(lhs.size() + rhs.size());
result.insert(result.cend(), lhs.cbegin(), lhs.cend());
result.insert(result.cend(), rhs.cbegin(), rhs.cend());
return result;
}
template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, const std::vector<T>& rhs)
{
lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
return std::move(lhs);
}
template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, std::vector<T>&& rhs)
{
rhs.insert(rhs.cbegin(), lhs.cbegin(), lhs.cend());
return std::move(rhs);
}
template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, std::vector<T>&& rhs)
{
if (lhs.empty()) return std::move(rhs);
lhs.insert(lhs.cend(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
return std::move(lhs);
}
Zauważ, że różni się to od append
ing do a vector
.
Możesz przygotować własny szablon dla operatora +:
template <typename T>
inline T operator+(const T & a, const T & b)
{
T res = a;
res.insert(res.end(), b.begin(), b.end());
return res;
}
Następna rzecz - wystarczy użyć +:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Ten przykład podaje dane wyjściowe:
1 2 3 4 5 6 7 8
T operator+(const T & a, const T & b)
jest niebezpieczne, lepiej jest używać vector<T> operator+(const vector<T> & a, const vector<T> & b)
.
Istnieje algorytm std::merge
z C ++ 17 , który jest bardzo łatwy w użyciu,
Poniżej znajduje się przykład:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
//DATA
std::vector<int> v1{2,4,6,8};
std::vector<int> v2{12,14,16,18};
//MERGE
std::vector<int> dst;
std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst));
//PRINT
for(auto item:dst)
std::cout<<item<<" ";
return 0;
}
std::vector::insert
, ale robi coś innego: łączenie dwóch zakresów w nowy zakres vs wstawianie jednego wektora na końcu innego. Warto wspomnieć w odpowiedzi?
Jeśli Twoim celem jest po prostu iteracja w zakresie wartości do celów tylko do odczytu, alternatywą jest zawinięcie obu wektorów wokół proxy (O (1)) zamiast kopiowania ich (O (n)), aby były natychmiast widoczne jako pojedynczy, ciągły.
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] << " "; // ----> 1 2 3 4 5 10 20 30
Więcej informacji, w tym implementację „VecProxy” oraz zalety i wady, można znaleźć na stronie https://stackoverflow.com/a/55838758/2379625 .
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
Zaimplementowałem tę funkcję, która łączy dowolną liczbę kontenerów, przechodząc od referencji do wartości i kopiując w inny sposób
namespace internal {
// Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if
// appropriate
template<typename Target, typename Head, typename... Tail>
void AppendNoReserve(Target* target, Head&& head, Tail&&... tail) {
// Currently, require each homogenous inputs. If there is demand, we could probably implement a
// version that outputs a vector whose value_type is the common_type of all the containers
// passed to it, and call it ConvertingConcatenate.
static_assert(
std::is_same_v<
typename std::decay_t<Target>::value_type,
typename std::decay_t<Head>::value_type>,
"Concatenate requires each container passed to it to have the same value_type");
if constexpr (std::is_lvalue_reference_v<Head>) {
std::copy(head.begin(), head.end(), std::back_inserter(*target));
} else {
std::move(head.begin(), head.end(), std::back_inserter(*target));
}
if constexpr (sizeof...(Tail) > 0) {
AppendNoReserve(target, std::forward<Tail>(tail)...);
}
}
template<typename Head, typename... Tail>
size_t TotalSize(const Head& head, const Tail&... tail) {
if constexpr (sizeof...(Tail) > 0) {
return head.size() + TotalSize(tail...);
} else {
return head.size();
}
}
} // namespace internal
/// Concatenate the provided containers into a single vector. Moves from rvalue references, copies
/// otherwise.
template<typename Head, typename... Tail>
auto Concatenate(Head&& head, Tail&&... tail) {
size_t totalSize = internal::TotalSize(head, tail...);
std::vector<typename std::decay_t<Head>::value_type> result;
result.reserve(totalSize);
internal::AppendNoReserve(&result, std::forward<Head>(head), std::forward<Tail>(tail)...);
return result;
}
Jeśli to, czego szukasz, to sposób na dołączenie wektora do innego po utworzeniu, vector::insert
to najlepiej postawić zakład, na co kilkakrotnie odpowiadano, na przykład:
vector<int> first = {13};
const vector<int> second = {42};
first.insert(first.end(), second.cbegin(), second.cend());
Niestety nie ma sposobu, aby zbudować const vector<int>
, ponieważ powyżej musisz zbudować, a następnie insert
.
Jeśli to, czego tak naprawdę szukasz, to pojemnik do połączenia tych dwóch vector<int>
s, może być dla ciebie coś lepszego, jeśli:
vector
zawiera prymitywyconst
pojemnikJeśli wszystkie powyższe są prawdziwe, sugeruję użycie basic_string
kto char_type
pasuje do rozmiaru pierwotnej zawartej w twoim vector
. static_assert
Aby sprawdzić, czy te rozmiary są spójne, należy wprowadzić w kodzie a:
static_assert(sizeof(char32_t) == sizeof(int));
Dzięki temu utrzymaniu prawdy możesz po prostu:
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
Aby uzyskać więcej informacji na temat różnic między string
i vector
można zajrzeć tutaj: https://stackoverflow.com/a/35558008/2642059
Przykład tego kodu na żywo można znaleźć tutaj: http://ideone.com/7Iww3I
To rozwiązanie może być nieco skomplikowane, ale boost-range
ma też kilka innych fajnych rzeczy do zaoferowania.
#include <iostream>
#include <vector>
#include <boost/range/algorithm/copy.hpp>
int main(int, char**) {
std::vector<int> a = { 1,2,3 };
std::vector<int> b = { 4,5,6 };
boost::copy(b, std::back_inserter(a));
for (auto& iter : a) {
std::cout << iter << " ";
}
return EXIT_SUCCESS;
}
Często intencją jest połączenie wektora a
i b
po prostu iteracja nad nim wykonując jakąś operację. W tym przypadku istnieje śmieszna prosta join
funkcja.
#include <iostream>
#include <vector>
#include <boost/range/join.hpp>
#include <boost/range/algorithm/copy.hpp>
int main(int, char**) {
std::vector<int> a = { 1,2,3 };
std::vector<int> b = { 4,5,6 };
std::vector<int> c = { 7,8,9 };
// Just creates an iterator
for (auto& iter : boost::join(a, boost::join(b, c))) {
std::cout << iter << " ";
}
std::cout << "\n";
// Can also be used to create a copy
std::vector<int> d;
boost::copy(boost::join(a, boost::join(b, c)), std::back_inserter(d));
for (auto& iter : d) {
std::cout << iter << " ";
}
return EXIT_SUCCESS;
}
W przypadku dużych wektorów może to być zaletą, ponieważ nie ma możliwości kopiowania. Można go również wykorzystać do łatwego kopiowania uogólnień do więcej niż jednego kontenera.
Z jakiegoś powodu nie ma czegoś takiego boost::join(a,b,c)
, co może być rozsądne.
Możesz to zrobić za pomocą wstępnie zaimplementowanych algorytmów STL, używając szablonu do zastosowania typu polimorficznego.
#include <iostream>
#include <vector>
#include <algorithm>
template<typename T>
void concat(std::vector<T>& valuesa, std::vector<T>& valuesb){
for_each(valuesb.begin(), valuesb.end(), [&](int value){ valuesa.push_back(value);});
}
int main()
{
std::vector<int> values_p={1,2,3,4,5};
std::vector<int> values_s={6,7};
concat(values_p, values_s);
for(auto& it : values_p){
std::cout<<it<<std::endl;
}
return 0;
}
Możesz usunąć drugi wektor, jeśli nie chcesz go dalej używać ( clear()
metoda).
Połącz dwa std::vector-s
z for
pętlą w jednymstd::vector
.
Przykład:
std::vector <int> v1 {1, 2, 3};//declare vector1
std::vector <int> v2 {4, 5};//declare vector2
std::vector <int> suma;//declare vector suma
for(auto i = 0; i < v1.size()-1;i++)//for loop 1
{
suma.push_back(v1[i]);
}
for(auto i = 0; i< v2.size()-1;i++)/for loop 2
{
suma.push_back(v2[i]);
}
for(auto i = 0; i < suma.size(); i++)//for loop 3-output
{
std::cout<<suma[i];
}
Wpisz ten kod w main()
.
for
pętle są błędne. Prawidłowe indeksy w wektorze wynoszą od 0 do size()-1
. Musisz zakończyć warunki zakończenia pętli i < v1.size()
, używając <
nie <=
. Zastosowanie złego stanu powoduje dostęp do pamięci poza kontenerem.
auto
iteratorów zamiast ręcznego indeksowania. Nie obchodzi Cię, jaki indeks łączysz, tylko że jest on wykonywany sekwencyjnie.
size()-1
w dwóch warunkach pętli? To pomija ostatnie elementy wektorowe. Trzecia pętla jest teraz jedyną poprawną.
Szczerze mówiąc, możesz szybko połączyć dwa wektory, kopiując elementy z dwóch wektorów do drugiego lub po prostu dołączając jeden z dwóch wektorów !. To zależy od twojego celu.
Metoda 1: Przypisz nowy wektor, którego rozmiar jest sumą rozmiaru dwóch oryginalnych wektorów.
vector<int> concat_vector = vector<int>();
concat_vector.setcapacity(vector_A.size() + vector_B.size());
// Loop for copy elements in two vectors into concat_vector
Metoda 2: Dołącz wektor A, dodając / wstawiając elementy wektora B.
// Loop for insert elements of vector_B into vector_A with insert()
function: vector_A.insert(vector_A .end(), vector_B.cbegin(), vector_B.cend());
std::move_iterator
, aby elementy były przenoszone zamiast kopiowane. (patrz en.cppreference.com/w/cpp/iterator/move_iterator ).
setcapacity
? Co to jest function:
?
resize
metodzie.