Bagaimana saya menggabungkan dua std::vector
s?
a + b
atau a.concat(b)
di perpustakaan standar? Mungkin implementasi default akan suboptimal, tetapi setiap rangkaian array tidak perlu dioptimalkan secara mikro
Bagaimana saya menggabungkan dua std::vector
s?
a + b
atau a.concat(b)
di perpustakaan standar? Mungkin implementasi default akan suboptimal, tetapi setiap rangkaian array tidak perlu dioptimalkan secara mikro
Jawaban:
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
reserve
vektor tujuan terlebih dahulu?
vector1.capacity() >= 2 * vector1.size()
. Yang tidak biasa kecuali Anda sudah menelepon std::vector::reserve()
. Kalau tidak, vektor akan realokasi, membatalkan iterator yang dilewatkan sebagai parameter 2 dan 3.
.concat
atau +=
atau sesuatu
Jika Anda menggunakan C ++ 11, dan ingin memindahkan elemen daripada hanya menyalinnya, Anda dapat menggunakannya std::move_iterator
bersama dengan menyisipkan (atau menyalin):
#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;
}
Ini tidak akan lebih efisien untuk contoh dengan int, karena memindahkannya tidak lebih efisien daripada menyalinnya, tetapi untuk struktur data dengan gerakan yang dioptimalkan, ia dapat menghindari menyalin keadaan yang tidak perlu:
#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;
}
Setelah pindah, elemen src dibiarkan dalam keadaan tidak terdefinisi tetapi aman untuk dihancurkan, dan elemen sebelumnya dipindahkan langsung ke elemen baru dest pada akhirnya.
std::move(src.begin(), src.end(), back_inserter(dest))
?
Saya akan menggunakan fungsi insert , seperti:
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Atau Anda bisa menggunakan:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
Pola ini berguna jika kedua vektor tidak mengandung jenis yang persis sama, karena Anda dapat menggunakan sesuatu daripada std :: back_inserter untuk mengonversi dari satu jenis ke yang lain.
reserve
dulu. Alasan std::copy
yang terkadang bermanfaat adalah jika Anda ingin menggunakan sesuatu selain back_inserter
.
Dengan C ++ 11, saya lebih suka mengikuti untuk menambahkan vektor b ke:
std::move(b.begin(), b.end(), std::back_inserter(a));
kapan a
dan b
tidak tumpang tindih, dan b
tidak akan digunakan lagi.
Ini std::move
dari <algorithm>
, tidak biasa std::move
dari <utility>
.
insert
cara lama yang lebih aman.
insert()
dengan dengan move_iterator
s? Jika ya, bagaimana caranya?
std::move
kita bicarakan di sini, karena kebanyakan orang tidak tahu kelebihan ini. Semoga ini perbaikan.
std::vector<int> first;
std::vector<int> second;
first.insert(first.end(), second.begin(), second.end());
Saya lebih suka yang sudah disebutkan:
a.insert(a.end(), b.begin(), b.end());
Tetapi jika Anda menggunakan C ++ 11, ada satu lagi cara umum:
a.insert(std::end(a), std::begin(b), std::end(b));
Juga, bukan bagian dari pertanyaan, tetapi disarankan untuk digunakan reserve
sebelum menambahkan untuk kinerja yang lebih baik. Dan jika Anda menggabungkan vektor dengan dirinya sendiri, tanpa menyimpannya gagal, maka Anda harus selalu melakukannya reserve
.
Jadi pada dasarnya yang Anda butuhkan:
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::
jika jenis a
berasal dari std
, yang mengalahkan aspek generik.
Anda harus menggunakan vector :: insert
v1.insert(v1.end(), v2.begin(), v2.end());
Peningkatan kinerja umum untuk concatenate adalah untuk memeriksa ukuran vektor. Dan gabungkan / masukkan yang lebih kecil dengan yang lebih besar.
//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()...
menggunakan iterator ke v2
untuk menentukan posisi dalam v1
.
Jika Anda ingin dapat menyatukan vektor secara ringkas, Anda dapat membebani +=
operator.
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;
}
Maka Anda dapat menyebutnya seperti ini:
vector1 += vector2;
Jika Anda tertarik dengan jaminan pengecualian yang kuat (ketika copy constructor dapat memberikan pengecualian):
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;
}
}
Mirip append_move
dengan jaminan yang kuat tidak dapat diterapkan secara umum jika konstruktor elemen bergerak dapat melempar (yang tidak mungkin tetapi tetap).
v1.erase(...
melempar juga?
insert
sudah menangani ini. Selain itu, panggilan ke erase
ini setara dengan a resize
.
Tambahkan yang ini ke file header Anda:
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;
}
dan gunakan seperti ini:
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 akan mengandung [1,2,62]
Berikut ini adalah solusi tujuan umum menggunakan semantik C ++ 11 move:
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);
}
Perhatikan bagaimana ini berbeda dari append
ing ke vector
.
Anda dapat menyiapkan templat Anda sendiri untuk + operator:
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;
}
Hal berikutnya - cukup gunakan +:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Contoh ini memberikan output:
1 2 3 4 5 6 7 8
T operator+(const T & a, const T & b)
berbahaya, lebih baik digunakan vector<T> operator+(const vector<T> & a, const vector<T> & b)
.
Ada algoritma std::merge
dari C ++ 17 , yang sangat mudah digunakan,
Di bawah ini adalah contohnya:
#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
, tetapi itu untuk sesuatu yang berbeda: menggabungkan dua rentang ke dalam rentang baru vs memasukkan satu vektor di ujung yang lain. Layak disebutkan dalam jawabannya?
Jika tujuan Anda hanya untuk mengulangi rentang nilai untuk tujuan hanya-baca, alternatifnya adalah membungkus kedua vektor di sekitar proksi (O (1)) alih-alih menyalinnya (O (n)), sehingga mereka segera terlihat sebagai satu, yang berdekatan.
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
Lihat https://stackoverflow.com/a/55838758/2379625 untuk rincian lebih lanjut, termasuk implementasi 'VecProxy' serta pro & kontra.
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
Saya telah mengimplementasikan fungsi ini yang menggabungkan sejumlah kontainer, bergerak dari rvalue-reference dan menyalin sebaliknya
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;
}
Jika yang Anda cari adalah cara untuk menambahkan vektor ke vektor lain setelah pembuatan, vector::insert
adalah taruhan terbaik Anda, seperti yang telah dijawab beberapa kali, misalnya:
vector<int> first = {13};
const vector<int> second = {42};
first.insert(first.end(), second.cbegin(), second.cend());
Sayangnya tidak ada cara untuk membangun const vector<int>
, karena di atas Anda harus membangun dan kemudian insert
.
Jika yang Anda cari sebenarnya adalah wadah untuk mengadakan pertemuan kedua vector<int>
, mungkin ada sesuatu yang lebih baik tersedia bagi Anda, jika:
vector
mengandung primitifconst
wadahJika semua di atas benar, saya sarankan menggunakan basic_string
yang char_type
cocok dengan ukuran primitif yang terkandung di dalam Anda vector
. Anda harus memasukkan static_assert
dalam kode Anda untuk memvalidasi ukuran ini tetap konsisten:
static_assert(sizeof(char32_t) == sizeof(int));
Dengan memegang ini benar Anda hanya dapat melakukan:
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
Untuk informasi lebih lanjut tentang perbedaan antara string
dan vector
Anda dapat melihat di sini: https://stackoverflow.com/a/35558008/2642059
Untuk contoh langsung dari kode ini Anda dapat melihat di sini: http://ideone.com/7Iww3I
Solusi ini mungkin agak rumit, tetapi boost-range
juga memiliki beberapa hal bagus untuk ditawarkan.
#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;
}
Seringkali niat adalah untuk menggabungkan vektor a
dan b
hanya mengulanginya melakukan beberapa operasi. Dalam hal ini, ada join
fungsi sederhana yang konyol .
#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;
}
Untuk vektor besar ini mungkin menguntungkan, karena tidak ada penyalinan. Dapat juga digunakan untuk menyalin generalisasi dengan mudah ke lebih dari satu wadah.
Untuk beberapa alasan tidak ada yang seperti itu boost::join(a,b,c)
, yang bisa masuk akal.
Anda dapat melakukannya dengan algoritma STL yang telah diimplementasikan menggunakan template untuk penggunaan tipe polimorfik.
#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;
}
Anda dapat menghapus vektor kedua jika Anda tidak ingin menggunakannya lebih lanjut ( clear()
metode).
Sejujurnya, Anda dapat dengan cepat menggabungkan dua vektor dengan menyalin elemen dari dua vektor ke vektor lainnya atau hanya menambahkan satu dari dua vektor !. Itu tergantung pada tujuan Anda.
Metode 1: Tetapkan vektor baru dengan ukurannya adalah jumlah dari dua ukuran vektor asli.
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
Metode 2: Tambahkan vektor A dengan menambahkan / memasukkan elemen vektor 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
agar elemen dipindahkan daripada disalin. (lihat en.cppreference.com/w/cpp/iterator/move_iterator ).
setcapacity
? Apa function:
?
resize
metode ini.