İki std::vector
s'yi nasıl birleştirebilirim ?
a + b
veya a.concat(b)
kütüphanede uygulanmadığını merak eden tek kişi ben miyim ? Belki de varsayılan uygulama yetersizdir, ancak her dizi birleştirmesinin mikro optimize edilmesine gerek yoktur
İki std::vector
s'yi nasıl birleştirebilirim ?
a + b
veya a.concat(b)
kütüphanede uygulanmadığını merak eden tek kişi ben miyim ? Belki de varsayılan uygulama yetersizdir, ancak her dizi birleştirmesinin mikro optimize edilmesine gerek yoktur
Yanıtlar:
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
reserve
, önce hedef vektörü çağırmak yardımcı olur mu?
vector1.capacity() >= 2 * vector1.size()
. Çağırmadıkça hangisi atipiktir std::vector::reserve()
. Aksi takdirde, vektör 2 ve 3 parametreleri olarak geçirilen yineleyicileri geçersiz kılarak yeniden tahsis edecektir
.concat
ya +=
da bir şey
C ++ 11 kullanıyorsanız ve öğeleri yalnızca kopyalamak yerine taşımak istiyorsanız, std::move_iterator
insert (veya copy) ile birlikte kullanabilirsiniz :
#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;
}
Bu, ints'lı örnek için daha verimli olmayacaktır, çünkü onları taşımak onları kopyalamaktan daha verimli değildir, ancak optimize edilmiş hareketlere sahip bir veri yapısı için gereksiz durumları kopyalamaktan kaçınabilir:
#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;
}
Taşınma işleminden sonra, src öğesi tanımlanmamış, ancak imha edilmesi güvenli bir durumda bırakılır ve eski öğeleri doğrudan dest'in yeni öğesine aktarılır.
std::move(src.begin(), src.end(), back_inserter(dest))
?
Ekle işlevini , gibi bir şey kullanmak istiyorsunuz :
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Veya şunları kullanabilirsiniz:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
Bu desen, iki vektör tamamen aynı türden bir şey içermiyorsa yararlıdır, çünkü bir türden diğerine dönüştürmek için std :: back_inserter yerine bir şey kullanabilirsiniz.
reserve
önce sizi aramayı durduran hiçbir şey yok . Sebebi std::copy
bazen yararlıdır, başka bir şey kullanmak istiyorsanız back_inserter
.
C ++ 11 ile, b'ye vektör eklemek için aşağıdakileri tercih ederim:
std::move(b.begin(), b.end(), std::back_inserter(a));
ne zaman a
ve b
üst üste değildir ve b
artık kullanılamaz gitmiyor.
Bu std::move
gelen <algorithm>
, değil zamanki std::move
dan <utility>
.
insert
daha güvenli olan eski yola geri dönmeliyim .
insert()
, move_iterator
s'den farklı mıdır ? Öyleyse nasıl?
std::move
Burada konuştuğumuz şey hakkında bir not ekledim , çünkü çoğu insan bu aşırı yüklenmeyi bilmiyor. Umarım bu bir gelişmedir.
std::vector<int> first;
std::vector<int> second;
first.insert(first.end(), second.begin(), second.end());
Zaten bahsedilen birini tercih ederim:
a.insert(a.end(), b.begin(), b.end());
Ancak C ++ 11 kullanıyorsanız, genel bir yol daha vardır:
a.insert(std::end(a), std::begin(b), std::end(b));
Ayrıca, bir sorunun parçası değil, ancak reserve
daha iyi performans için eklenmeden önce kullanılması önerilir . Ve vektörü kendisiyle birleştiriyorsanız, ayırmadan başarısız olur, bu yüzden her zaman yapmalısınız reserve
.
Temel olarak ihtiyacınız olan şey:
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::
türü ise a
gelen std
jenerik yönü defeats.
Vector :: insert kullanmalısınız
v1.insert(v1.end(), v2.begin(), v2.end());
Bir genel performansı artırmak BİRLEŞTİR için vektörlerin boyutunu kontrol etmektir. Ve küçük olanı büyük olanla birleştirin / yerleştirin.
//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()...
içine bir yineleyici kullanıyor . v2
v1
Vektörleri kısaca birleştirmek istiyorsanız, +=
operatörü aşırı yükleyebilirsiniz .
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;
}
Sonra şöyle diyebilirsiniz:
vector1 += vector2;
Güçlü istisna garantisiyle ilgileniyorsanız (kopya oluşturucu bir istisna atabildiğinde):
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;
}
}
append_move
Vektör öğesinin hareket yapıcısı fırlatabiliyorsa (bu olası değil ancak yine de) genel olarak güçlü garantiye benzer .
v1.erase(...
mi?
insert
zaten bunu hallediyor. Ayrıca, bu çağrı erase
bir resize
.
Bunu başlık dosyanıza ekleyin:
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;
}
ve şu şekilde kullanın:
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 içerecek [1,2,62]
İşte C ++ 11 hareket semantiğini kullanan genel amaçlı bir çözüm:
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);
}
Bunun ing'den a'ya ne kadar farklı append
olduğuna dikkat edin vector
.
+ Operatör için kendi şablonunuzu hazırlayabilirsiniz:
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;
}
Sonraki şey - sadece + kullanın:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Bu örnek çıktı verir:
1 2 3 4 5 6 7 8
T operator+(const T & a, const T & b)
tehlikelidir, kullanmak daha iyidir vector<T> operator+(const vector<T> & a, const vector<T> & b)
.
Bir algoritma vardır std::merge
gelen C ++ 17 çok kolay olan,
Aşağıda örnek:
#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
, ama farklı bir şey yapıyor: iki aralığı yeni bir aralığa birleştirmek vs bir diğerinin sonuna bir vektör eklemek. Cevapta bahsetmeye değer mi?
Hedefiniz salt okunur amaçlar için değer aralığını yinelemekse, alternatif her iki vektörü de kopyalamak yerine (O (n)) bir proxy (O (1)) etrafına sarmaktır, böylece hemen görülürler. tek bir bitişik olarak.
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
'VecProxy' uygulamasının yanı sıra artıları ve eksileri de dahil olmak üzere daha fazla ayrıntı için https://stackoverflow.com/a/55838758/2379625 adresine bakın .
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
Herhangi bir sayıda kapsayıcıyı birleştiren, rvalue referanslarından hareket eden ve başka şekilde kopyalama yapan bu işlevi uyguladım
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;
}
Aradığınız şey, oluşturulduktan sonra bir başkasına bir vektör eklemenin bir vector::insert
yoluysa, birkaç kez yanıtlandığı gibi en iyi bahsinizdir, örneğin:
vector<int> first = {13};
const vector<int> second = {42};
first.insert(first.end(), second.cbegin(), second.cend());
Ne yazık ki a'yı inşa etmenin bir yolu yok const vector<int>
, yukarıdaki gibi ve sonra yapmalısınız insert
.
Aslında aradığınız vector<int>
şey, bu ikisinin birleşimini barındıracak bir kapsa, şu durumlarda daha iyi bir şey olabilir:
vector
ilkelleriniz içeriyorconst
kap istiyorsunYukarıdakilerin hepsi doğruysa, basic_string
kimin char_type
içinde bulunan ilkelin boyutuna kimin uyduğunu kullanmanızı öneririm vector
. static_assert
Bu boyutların tutarlı kalmasını doğrulamak için kodunuza bir dahil etmelisiniz :
static_assert(sizeof(char32_t) == sizeof(int));
Bu doğru tutma ile şunları yapabilirsiniz:
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
Arasındaki farklar hakkında daha fazla bilgi için string
ve vector
burada bakabilirsiniz: https://stackoverflow.com/a/35558008/2642059
Bu kodun canlı bir örneği için buraya bakabilirsiniz: http://ideone.com/7Iww3I
Bu çözüm biraz karmaşık olabilir, ancak boost-range
sunabileceği başka güzel şeyler de vardır.
#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;
}
Çoğu zaman amaç vektörü birleştirmek a
ve b
sadece bir işlem yaparak onu tekrarlamaktır. Bu durumda, saçma basit bir join
işlev var.
#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;
}
Büyük vektörler için bu bir avantaj olabilir, çünkü kopyalama yoktur. Ayrıca, genellemeleri birden fazla kaba kolayca kopyalamak için de kullanılabilir.
Bazı nedenlerden dolayı boost::join(a,b,c)
makul olabilecek bir şey yoktur .
Bir polimorfik tip kullanım için bir şablon kullanarak önceden uygulanmış STL algoritmaları ile yapabilirsiniz.
#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;
}
Daha fazla kullanmak istemiyorsanız ikinci vektörü silebilirsiniz ( clear()
yöntem).
Bitiştirmek iki std::vector-s
ile for
bir döngü std::vector
.
Misal:
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(int i = 0; i < v1.size()-1;i++)//for loop 1
{
suma.push_back(v1[i]);
}
for(int i = 0; i< v2.size()-1;i++)/for loop 2
{
suma.push_back(v2[i]);
}
for(int i = 0; i < suma.size(); i++)//for loop 3-output
{
std::cout<<suma[i];
}
Bu kodu içine yazın main()
.
for
döngüler yanlış. Bir vektördeki geçerli indeksler 0 ila arasındadır size()-1
. Sen döngü sonlandırma koşulları değil i < v1.size()
, olmalıdır . Yanlış koşul kullanılması kabın dışındaki belleğe erişir. <
<=
auto
manuel dizinleme yerine yineleyiciler kullanıyor olmalısınız . Hangi dizini birleştirdiğinizi umursamazsınız, yalnızca dizinin sıralı olarak yapılması gerekir.
size()-1
iki döngü koşulunuzda kullandığınızı açıklayabilir misiniz ? Bu son vektör elemanlarını atlar. Üçüncü döngü şimdi tek doğru olanıdır.
Dürüst olmak gerekirse, iki vektörü iki vektörden diğerine kopyalayarak iki vektörü hızlı bir şekilde birleştirebilir veya sadece iki vektörden birini ekleyebilirsiniz !. Bu sizin amacınıza bağlıdır.
Yöntem 1: Boyutu ile yeni vektör atamak iki orijinal vektör boyutu toplamıdır.
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
Yöntem 2: B vektörünün öğelerini ekleyerek / ekleyerek A vektörünü ekleyin.
// 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
öğelerin kopyalanmak yerine taşınması daha iyi olabilir . (bkz. en.cppreference.com/w/cpp/iterator/move_iterator ).
setcapacity
? Nedir function:
?
resize
yöntem hakkında konuşuyor .