Menggabungkan dua std :: vektor


686

Bagaimana saya menggabungkan dua std::vectors?


5
Jawaban yang diberikan tidak benar-benar digabungkan. Mereka menambahkan salinan. Mungkin ada gunanya (untuk sudut pandang efisiensi) untuk membuat metode gabungan std :: vector, namun itu akan memerlukan beberapa berbagi canggih pengelolaan node dan itu mungkin mengapa itu belum dilakukan.
FauChristian

8
@ Fu christian: Tidak, mungkin tidak ada gunanya dari sudut pandang efisiensi. Memori vektor harus kontinu, sehingga apa yang Anda sarankan tidak mungkin. Jika Anda ingin "berbagi canggih pengelolaan node", dan jika Anda mengubah kelas vektor sedemikian rupa, Anda akan berakhir dengan deque. Bahkan kemudian sangat sulit untuk menggunakan kembali memori dengan cara yang disarankan, meskipun itu akan mulai menjadi sedikit lebih layak. Saya tidak berpikir ini sedang dilaksanakan. Hal utama adalah bahwa dalam pembagian node manajemen (deque) seperti itu, node akhir mungkin sebagian kosong.
Kue

4
@lecaruyer Anda menyadari bahwa Anda baru saja menandai pertanyaan yang diajukan dua tahun sebelumnya sebagai duplikat
eshirima

9
Apakah saya satu-satunya yang bertanya-tanya mengapa ini tidak diimplementasikan sebagai a + batau a.concat(b)di perpustakaan standar? Mungkin implementasi default akan suboptimal, tetapi setiap rangkaian array tidak perlu dioptimalkan secara mikro
oseiskar

9
evolusi selama bertahun-tahun, kelebihan-operator yang paling canggih dari semua bahasa utama, sistem templating yang menggandakan kompleksitas bahasa, namun jawabannya bukan v = v1 + v2;
Spike0xff

Jawaban:


726
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );

53
Saya hanya akan menambahkan kode untuk pertama-tama mendapatkan jumlah elemen yang masing-masing vektor pegang, dan mengatur vector1 menjadi yang memegang terbesar. Jika Anda melakukan sebaliknya Anda melakukan banyak penyalinan yang tidak perlu.
Joe Pineda

34
Saya punya pertanyaan. Apakah ini akan berfungsi jika vector1 dan vector2 adalah vektor yang sama?
Alexander Rafferty

6
Jika Anda telah menyatukan beberapa vektor ke satu, apakah sebaiknya memanggil reservevektor tujuan terlebih dahulu?
Faheem Mitha

33
@AlexanderRafferty: Hanya jika 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.
Drew Dormann

28
Sayang sekali tidak ada ekspresi yang lebih ringkas di perpustakaan standar. .concatatau +=atau sesuatu
nmr

193

Jika Anda menggunakan C ++ 11, dan ingin memindahkan elemen daripada hanya menyalinnya, Anda dapat menggunakannya std::move_iteratorbersama 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.


6
Metode std :: make_move_iterator () membantu saya ketika mencoba menggabungkan std :: vektor std :: unique_ptr.
Knitschi

Apa perbedaan antara ini dan std::move(src.begin(), src.end(), back_inserter(dest))?
kshenoy


77

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.


7
metode salin bukan cara yang baik. Ini akan memanggil beberapa waktu push_back yang berarti bahwa jika banyak elemen harus dimasukkan ini bisa berarti beberapa realokasi. lebih baik menggunakan insert karena implementasi vektor dapat melakukan beberapa optimasi untuk menghindari realokasi. itu bisa menyimpan memori sebelum mulai menyalin
Yogesh Arora

7
@Yogesh: memang, tapi tidak ada yang menghentikan Anda menelepon reservedulu. Alasan std::copyyang terkadang bermanfaat adalah jika Anda ingin menggunakan sesuatu selain back_inserter.
Roger Lipscombe

Ketika Anda mengatakan "alokasi ganda", itu benar - tetapi jumlah alokasi adalah log terburuk (jumlah entri ditambahkan) - yang berarti bahwa biaya menambahkan entri adalah konstan dalam jumlah entri yang ditambahkan. (Pada dasarnya, jangan khawatir tentang hal itu kecuali membuat profil menunjukkan Anda memerlukan cadangan).
Martin Bonner mendukung Monica

Anda mungkin ingin menggunakan std :: transform untuk melakukan ini.
Martin Broadhurst

1
copy banyak, bahkan dengan cadangan. vector :: insert akan menghindari semua pemeriksaan: quick-bench.com/bLJO4OfkAzMcWia7Pa80ynwmAIA
Denis Yaroshevskiy

63

Dengan C ++ 11, saya lebih suka mengikuti untuk menambahkan vektor b ke:

std::move(b.begin(), b.end(), std::back_inserter(a));

kapan adan btidak tumpang tindih, dan btidak akan digunakan lagi.


Ini std::movedari <algorithm>, tidak biasa std::move dari <utility>.


10
Perilaku tidak terdefinisi jika a sebenarnya adalah b (yang OK jika Anda tahu itu tidak akan pernah terjadi - tetapi perlu diperhatikan dalam kode tujuan umum).
Martin Bonner mendukung Monica

1
@ MartinBonner Terima kasih telah menyebutkan itu. Mungkin saya harus kembali ke insertcara lama yang lebih aman.
Deqing

15
Ah, std LAIN :: bergerak. Cukup membingungkan saat pertama kali Anda melihatnya.
xaxxon

1
Apakah ini berbeda insert()dengan dengan move_iterators? Jika ya, bagaimana caranya?
GPhilo

1
Saya telah menambahkan catatan tentang apa yang std::movekita bicarakan di sini, karena kebanyakan orang tidak tahu kelebihan ini. Semoga ini perbaikan.
YSC

38
std::vector<int> first;
std::vector<int> second;

first.insert(first.end(), second.begin(), second.end());

24

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

2
std::disimpulkan melalui pencarian argumen-dependen . end(a)akan cukup.
Asu

4
@Asu ADL hanya akan menambahkan std::jika jenis aberasal dari std, yang mengalahkan aspek generik.
Potatoswatter

Poin yang bagus. dalam hal ini vektor sehingga akan bekerja, tapi ya itu solusi yang lebih baik.
Asu

std :: begin () / end () ditambahkan untuk koleksi (seperti array) yang tidak memilikinya sebagai fungsi anggota. Tetapi array juga tidak memiliki fungsi anggota insert (), dan menyebut pertanyaan "Apakah ada koleksi dengan insert () tetapi tanpa begin () (yang berfungsi dengan std :: begin ())?"
James Curran


12

Anda harus menggunakan vector :: insert

v1.insert(v1.end(), v2.begin(), v2.end());

2
Bukankah ini sama dengan jawaban yang diberikan oleh Tom Ritter dan Robert Gamble pada 2008?
IgNite

9

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

Sangat sederhana, namun saya tidak pernah berpikir seperti itu!
Zimano

2
Kode sampel salah. v1.insert(v2.end()...menggunakan iterator ke v2untuk menentukan posisi dalam v1.
David Stone

Anda juga dapat menggunakan swap cepat. @ Davidvidone saya mengeditnya sehingga urutan concat dapat diubah. Apakah mungkin untuk menambahkan awal vektor?
qwr

Anda dapat menyisipkan ke awal, tetapi itu akan lebih lambat. Namun, untuk benar-benar "menyatukan", urutan biasanya penting, jadi itulah yang perlu Anda lakukan.
David Stone

7

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;

6

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_movedengan jaminan yang kuat tidak dapat diterapkan secara umum jika konstruktor elemen bergerak dapat melempar (yang tidak mungkin tetapi tetap).


Apakah tidak mungkin v1.erase(...melempar juga?
Kerangka Kelas

insertsudah menangani ini. Selain itu, panggilan ke eraseini setara dengan a resize.
Potatoswatter

Saya suka ini - terima kasih!
natersoz

5

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]


Tidak tahu mengapa ini tidak dipilih. Ini mungkin bukan cara paling efisien untuk melakukan ini, tetapi itu tidak salah dan efektif.
leeor_net

4

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 appending ke vector.


4

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

2
Menggunakan T operator+(const T & a, const T & b)berbahaya, lebih baik digunakan vector<T> operator+(const vector<T> & a, const vector<T> & b).
Matthieu H

4

Ada algoritma std::mergedari 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;
}

2
Saya tidak berpikir itu lebih mudah untuk digunakan daripada 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?
jb

4

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.


3
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));

6
Meskipun potongan kode ini dapat menyelesaikan masalah, itu tidak menjelaskan mengapa atau bagaimana ia menjawab pertanyaan. Harap sertakan penjelasan untuk kode Anda , karena itu sangat membantu untuk meningkatkan kualitas posting Anda. Penanda / pengulas: Untuk jawaban hanya kode seperti ini, downvote, jangan hapus! (Catatan: Jawaban ini mungkin sebenarnya cukup sederhana untuk membuat penjelasan, dan dengan demikian menurunkan, tidak perlu. Anda mungkin masih ingin menambahkan penjelasan untuk mencegah lebih banyak bendera NAA / VLQ.)
Scott Weldon

2

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

1

Jika yang Anda cari adalah cara untuk menambahkan vektor ke vektor lain setelah pembuatan, vector::insertadalah 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:

  1. Anda vectormengandung primitif
  2. Primitif Anda yang terkandung berukuran 32-bit atau lebih kecil
  3. Anda menginginkan sebuah constwadah

Jika semua di atas benar, saya sarankan menggunakan basic_stringyang char_typecocok dengan ukuran primitif yang terkandung di dalam Anda vector. Anda harus memasukkan static_assertdalam 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 stringdan vectorAnda 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


0

Solusi ini mungkin agak rumit, tetapi boost-rangejuga 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 adan bhanya mengulanginya melakukan beberapa operasi. Dalam hal ini, ada joinfungsi 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.


0

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).


-3

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

4
Apa yang ditambahkan jawaban Anda yang belum disediakan di jawaban lain?
Mat

13
@Mat: Karakter tebal.
marcv81

Jika vektor asli tidak diperlukan lagi setelahnya, mungkin lebih baik digunakan std::move_iteratoragar elemen dipindahkan daripada disalin. (lihat en.cppreference.com/w/cpp/iterator/move_iterator ).
tmlen

Apa setcapacity? Apa function: ?
LF

@ Jika saya pikir dia berbicara tentang resizemetode ini.
Matthieu H
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.