Bir vektörden bir alt vektör almanın en iyi yolu?


295

Bir boyutum var std::vector(diyelim myVec) N. X ile Y arasındaki elemanların bir kopyasından oluşan yeni bir vektör oluşturmanın en basit yolu nedir, burada 0 <= X <= Y <= N-1? Örneğin, myVec [100000]içinden myVec [100999]boyutta bir vektör içinde 150000.

Bu bir vektör ile verimli bir şekilde yapılamazsa, bunun yerine kullanmam gereken başka bir STL veri türü var mı?


7
bir altvektörü çıkarmak istediğinizi söylüyorsunuz, ama bana göre gerçekten istediğiniz şey altvektöre bir görünüm / erişim - bir görünüm kopyalanmayacak olan fark - eski okul C ++ başlangıç ​​işaretçisi ve bitiş işaretçisi kullanmak, bir std :: vektör üzerindeki memün bitişik olduğu gerçeği göz önüne alındığında, işaretçileri kullanarak yinelemeniz ve böylece kopyadan kaçınmanız mümkün olmalıdır, ancak kopyalamaya aldırmazsanız, bir önceki vektörün kapsamıyla yeni bir vektör başlatın vektör
serup

C ++ 11'den beri .data () ( cplusplus.com/reference/vector/vector/data ) vardır. Bununla birlikte, stl kapları içinde işaretçiler kullanmak önerilmez, bkz. Stackoverflow.com/questions/31663770/…
David Tóth

Yanıtlar:


372
vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
vector<T> newVec(first, last);

Yeni vektörü oluşturmak için O (N) işlemi, ama gerçekten daha iyi bir yol yok.


12
+1, ayrıca O (YX) 'den daha az veya ona eşit olan O (YX) (ve örneğinde çok daha az)
21'de orip

74
@orip Eh, o zaman O (N).
Johann Gerell

55
@GregRogers: N'nin belirli bir sayı olduğu big-O gösterimini kullanmak mantıklı değil. Big-O, büyüme oranını N'nin nasıl değiştiğine göre bildirir. Johann: Bir değişken adını iki şekilde kullanmamak en iyisidir. Normalde O(Y-X)biz de söylerdik ya da söylerdik O(Z) where Z=Y-X.
Mooing Duck

2
@GregRogers Bu yolu kullanarak yeni bir vektör bildirmemiz gerekiyor. Orijinal vektörü değiştirmenin bir yolu var mı? myVec gibi bir şey (ilk, son)? Bunun yanlış olduğunu biliyorum, ama kodlarımda özyineleme kullanmak istiyorum ve tekrar tekrar aynı vektör (değişmesine rağmen) kullanmanız gerekir gibi gerçekten çözüm gerekir. Teşekkürler!
ulyssis2

13
Neden sadece vector<T> newVec(myVec.begin() + 100000, myVec.begin() + 101000);?
aquirdturtle

88

Sadece vektör yapıcısını kullanın.

std::vector<int>   data();
// Load Z elements into data so that Z > Y > X

std::vector<int>   sub(&data[100000],&data[101000]);

2
Tamam, keyfi bir vektör öğesinden bir yineleyici almanın o kadar basit olduğunu fark etmedim.
An̲̳̳drew

5
Bu vektör öğelerinin adresini almak, vektör depolaması aslında bitişik değilse kırılacak olan taşınabilir olmayan bir saldırıdır.
Begin

2
Kötü, görünüşe göre standart vektör depolamanın bitişik olduğunu garanti ediyor. Bununla birlikte, rasgele erişimi destekleyen tüm kaplarda çalışacağı kesin olarak garanti edilmediğinden, böyle adreslerle çalışmak kötü bir uygulamadır, ancak (100 + 100000).
j_random_hacker

33
@j_random_hacker: Üzgünüm katılmıyorum. Std :: vector için STL spesifikasyonu bu tip prosedürü destekleyecek şekilde açıkça değiştirilmiştir. Ayrıca bir işaretçi geçerli yineleyici türüdür. İterator_traits yukarı bak <>
Martin York

6
@ taktak004 Hayır. Bunun operator[]bir referans döndürdüğünü unutmayın . Yalnızca referansı okuduğunuz veya yazdığınız noktada erişim ihlali haline gelecektir. İkisini de yapmamamız yerine adresi almamamız nedeniyle UB'yi çağırmadık.
Martin York

28

std::vector<T>(input_iterator, input_iterator), sizin durumunuzda foo = std::vector<T>(myVec.begin () + 100000, myVec.begin () + 150000);, örneğin buraya bakın


1
Andrew yeni bir vektör oluşturmaya çalıştığından, "foo = std :: vector (..." ile kopyalama yerine "std :: vector foo (...")
öneririm

4
Evet, elbette, ama std :: vector <int> foo = std :: vector (...) veya std :: vector <int> foo (...) yazmanız fark etmez.
Anteru

19

Bugünlerde spans kullanıyoruz! Yani şunu yazardınız:

#include <gsl/span>

...
auto start_pos = 100000;
auto length = 1000;
auto span_of_myvec = gsl::make_span(myvec);
auto my_subspan = span_of_myvec.subspan(start_pos, length);

ile aynı türden 1000 öğeden oluşan bir yayılma alanı elde etmek myvec. Veya daha kısa bir form:

auto my_subspan = gsl::make_span(myvec).subspan(1000000, 1000);

(ancak bunu sevmiyorum, çünkü her bir sayısal argümanın anlamı tamamen net değil; ve length ve start_pos aynı büyüklükte ise daha da kötüleşir.)

Her neyse, bunun bir kopya olmadığını, sadece vektördeki verilerin bir görünümü olduğunu unutmayın, bu yüzden dikkatli olun. Gerçek bir kopya istiyorsanız, şunları yapabilirsiniz:

std::vector<T> new_vec(my_subspan.cbegin(), my_subspan.cend());

Notlar:


kullanır cbeginve cendsadece prensip için;) std::cbeginvb.
JHBonarius

1
@JHBonarius: Bu kodun kapsayıcı seçiminde nasıl tempüle olmadığını görmek, belirli bir fayda olduğunu görmüyorum; sanırım bir tat meselesi.
einpoklum

11

Her iki modifiye edilecek değilseniz (hiç ürün ekleme / silme - Varolan değiştirerek sürece parçacığı konulara dikkat etmek olarak gayet), sadece etrafında geçebilir data.begin() + 100000ve data.begin() + 101000ve onlar olduklarını iddia begin()ve end()daha küçük bir vektör.

Ya da, vektör depolamanın bitişik olduğu garanti edildiğinden, 1000 öğelik bir dizinin etrafından geçebilirsiniz:

T *arrayOfT = &data[0] + 100000;
size_t arrayOfTLength = 1000;

Her iki teknik de sabit zaman alır, ancak verilerin uzunluğunun artmamasını gerektirir ve yeniden tahsisi tetikler.


Orijinal vektörün ve alt vektörün bağlanmasını istiyorsanız bu da iyidir.
PyRulez

8

Bu tartışma oldukça eskidir, ancak listenin başlatılmasıyla en basitinden henüz bahsedilmemiştir :

 vector<int> subvector = {big_vector.begin() + 3, big_vector.end() - 2}; 

C ++ 11 veya üstünü gerektirir.

Örnek kullanım:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main(){

    vector<int> big_vector = {5,12,4,6,7,8,9,9,31,1,1,5,76,78,8};
    vector<int> subvector = {big_vector.begin() + 3, big_vector.end() - 2};

    cout << "Big vector: ";
    for_each(big_vector.begin(), big_vector.end(),[](int number){cout << number << ";";});
    cout << endl << "Subvector: ";
    for_each(subvector.begin(), subvector.end(),[](int number){cout << number << ";";});
    cout << endl;
}

Sonuç:

Big vector: 5;12;4;6;7;8;9;9;31;1;1;5;76;78;8;
Subvector: 6;7;8;9;9;31;1;1;5;76;

6

Ne türden bahsetmediniz std::vector<...> myVec, ancak işaretçiler içermeyen basit bir tür veya yapı / sınıfsa ve en iyi verimliliği istiyorsanız, doğrudan bellek kopyası yapabilirsiniz (ki bence verilen diğer cevaplar). İşte bu durumda std::vector<type> myVecnerede olduğuna dair genel bir örnek :typeint

typedef int type; //choose your custom type/struct/class
int iFirst = 100000; //first index to copy
int iLast = 101000; //last index + 1
int iLen = iLast - iFirst;
std::vector<type> newVec;
newVec.resize(iLen); //pre-allocate the space needed to write the data directly
memcpy(&newVec[0], &myVec[iFirst], iLen*sizeof(type)); //write directly to destination buffer from source buffer

2
Acaba -O3, @ Anteru'nun "yapıcısını kullanarak" std::vector(myVec.begin () + 100000, myVec.begin () + 150000);, bu uzun versiyonun tam olarak aynı meclise girip girmeyeceğini merak ediyor muyum?
sandthorn

1
MSVC ++ 2015 örneğin, derlenir std::vector<>(iter, iter)için memmove(), eğer uygunsa, (yapıcı önemsiz uygun bir tanımı için, önemsiz ise).
Pablo H

1
Arama memcpy. std::copyBir aralığı kabul eden bir veya bir yapıcı (iki yineleyici) yapın; derleyici ve std.library memcpyuygun olduğunda çağırmak için komplo kuracaktır .
Bulletmagnet

4

Sadece kullanabilirsin insert

vector<type> myVec { n_elements };

vector<type> newVec;

newVec.insert(newVec.begin(), myVec.begin() + X, myVec.begin() + Y);

3

Sen kullanabilirsiniz STL kopyasını M subvector boyutudur zaman O (M) performansı ile.


Beni doğru yöne doğru işaret etti, ancak @LokiAstari'nin neden doğru seçim olmadığını öne sürdüğünü görebiliyorum - çünkü STL :: copy aynı boyutta ve tipte iki std :: vector <T> dizisiyle çalışıyor. Burada OP, OP'nin yayınında belirtildiği gibi bir alt bölümü yeni, daha küçük bir diziye kopyalamak istiyor: "0 <= X <= Y <= N-1"
Andrew

@Andrew, std :: copy ve std :: back_inserter
chrisg

@LokiAstari neden olmasın?
chrisg

2
@LokiAstari Bunu, akran incelemesinden sağ çıkamayan bir düzenlemeye atıfta bulunuyordum, bu örnekte ortaya çıkan <br/> vektör <T> newvec; std :: copy (myvec.begin () + 10000, myvec.begin () +10100, std :: back_inserter (newvec)); Bu durumda, önce hedefi inşa etmenize gerek yoktur, ancak emin olun, doğrudan başlatma daha fazladır ... doğrudan.
chrisg

1
@chrisg: İki çizgisi de var. Ek olarak, verimli olduğundan emin olmak için üçüncü bir satırı yapıştırmanız gerekir. newvec.reserve(10100 - 10000);. Kesinlikle bir seçenek ve teknik olarak işe yarayacak. Ama ikisinden hangisini tavsiye edeceksiniz?
Martin York

1

Doğrusal olmayan bir koleksiyonu yansıtmanın tek yolu, sonuçta ortaya çıkan "vektör "'ün aslında orijinal koleksiyona delege olan bir alt tip olduğu tembel olarak yapmaktır. Örneğin, Scala'nınList#subseq yöntemi sabit zamanda bir alt dizi oluşturur. Ancak, bu sadece koleksiyon değişmezse ve temel dil çöp toplama sporu yapıyorsa çalışır.


Bunu yapmak için c ++ yolunda X vektörü yerine X'e paylaşılan_ptr vektörüne sahip olmak ve ardından SP'leri kopyalamak olacaktır, ancak ne yazık ki bunun daha hızlı olduğunu düşünmüyorum çünkü SP işlemi cpying ile ilgili. Ya da orijinal vektör bunun yerine bir const shared_ptr vektörü olabilir ve sadece içinde alt aralıklara referans alırsınız. ofc'yi vektörün paylaşılan_ptr'i yapmak zorunda değilsiniz ama sonra ömür boyu problemleriniz var ... tüm bunlar kafamın üstünde, yanlış olabilir ...
NoSenseEtAl

0

Bunu sadece diğerleri için geç gönderiyoruz ... Bahse girerim ilk kodlayıcı şu ana kadar yapılıyor. Basit veri tipleri için kopyalamaya gerek yoktur, sadece eski C kodu yöntemlerine dönün.

std::vector <int>   myVec;
int *p;
// Add some data here and set start, then
p=myVec.data()+start;

Ardından p işaretçisini ve bir len'i bir alt sektöre ihtiyaç duyan herhangi bir şeye geçirin.

notelen olmalı !! len < myVec.size()-start


Bu bir kopyasını yapmaz.
Trilarion

0

Belki GSL kütüphanesindeki array_view / span iyi bir seçenektir.

İşte aynı zamanda tek bir dosya uygulaması: array_view .


Lütfen bağlantı ile birlikte cevap ekleyin. Dış bağlantı gelecekte değişebileceği için
Panther

0

Öğeleri bir vektörden diğerine kolayca kopyalayın
Bu örnekte, anlaşılmasını kolaylaştırmak için bir çift vektör kullanıyorum
.

vector<pair<int, int> > v(n);

//we want half of elements in vector a and another half in vector b
vector<pair<lli, lli> > a(v.begin(),v.begin()+n/2);
vector<pair<lli, lli> > b(v.begin()+n/2, v.end());


//if v = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
//then a = [(1, 2), (2, 3)]
//and b = [(3, 4), (4, 5), (5, 6)]

//if v = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7)]
//then a = [(1, 2), (2, 3), (3, 4)]
//and b = [(4, 5), (5, 6), (6, 7)]

'
Eğer o zaman kullanırsınız örneğin 16 endeksi 10 öğeleri kopyalamak istiyorsanız kolayca başka bir vektör öğeleri kopyalayabilirsiniz görebileceğiniz gibi

vector<pair<int, int> > a(v.begin()+10, v.begin+16);

ve dizin 10'dan bir dizinin sonuna kadar bazı öğeler istiyorsanız, bu durumda

vector<pair<int, int> > a(v.begin()+10, v.end()-5);

umarım bu yardımcı olur, sadece son durumda hatırla v.end()-5 > v.begin()+10


0

Yine başka bir seçenek: Örneğin, yapıcıyı kullanamayacağınız a thrust::device_vectorve a arasında hareket ederken thrust::host_vectorkullanışlıdır.

std::vector<T> newVector;
newVector.reserve(1000);
std::copy_n(&vec[100000], 1000, std::back_inserter(newVector));

Ayrıca karmaşıklık O (N) olmalıdır

Bunu üst anwer koduyla birleştirebilirsiniz

vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
std::copy(first, last, std::back_inserter(newVector));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.