वेक्टर से सबवेक्टर निकालने का सबसे अच्छा तरीका?


295

मान लीजिए मेरे पास आकार है std::vector(चलो इसे कॉल करें myVec) N। Y के माध्यम से तत्वों X की एक प्रतिलिपि से मिलकर एक नया वेक्टर बनाने का सबसे सरल तरीका क्या है, जहां 0 <= X <= Y <= N-1? उदाहरण के लिए, आकार के एक वेक्टर में के myVec [100000]माध्यम से ।myVec [100999]150000

यदि यह एक वेक्टर के साथ कुशलता से नहीं किया जा सकता है, तो क्या एक और एसटीएल डेटाटाइप है जिसे मुझे इसके बजाय उपयोग करना चाहिए?


7
आप कहते हैं कि आप एक सबवेक्टर निकालना चाहते हैं, लेकिन यह मुझे लगता है कि आप वास्तव में जो चाहते हैं, वह सबवेक्टर के लिए एक दृश्य / पहुंच है - अंतर यह है कि एक दृश्य कॉपी नहीं होगा - पुराने स्कूल सी ++ स्टार्ट पॉइंटर और एंड पॉइंटर का उपयोग करना होगा इस तथ्य को देखते हुए कि एक std पर :: :: वेक्टर सन्निहित है, तो आपके लिए पॉइंटर्स का उपयोग करके पुनरावृति करना संभव होना चाहिए और इस प्रकार कॉपी से बचना चाहिए, हालाँकि यदि आप कॉपी करने से बुरा नहीं मानते हैं, तो अपने पिछले हिस्से के दायरे के साथ एक नए वेक्टर को इनिशियलाइज़ करें। वेक्टर
नागिन

C ++ 11 के बाद से .data () ( cplusplus.com/reference/vector/vector/data ) है। हालाँकि, पॉइंटर्स का उपयोग करना stl कंटेनरों के भीतर हतोत्साहित करता है, देखें stackoverflow.com/questions/31663770/…
डेविड टोह

जवाबों:


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

नए वेक्टर के निर्माण के लिए यह एक O (N) ऑपरेशन है, लेकिन वास्तव में एक बेहतर तरीका नहीं है।


12
+1, यह भी O (YX) है, जो O (N) से कम या बराबर है (और उसके उदाहरण में बहुत कम)
orip

74
@orip खैर, फिर यह O (N) है।
जोहान जेरेल

55
@GregRogers: यह बड़े-ओ नोटेशन का उपयोग करने के लिए समझ में नहीं आता है जहां एन एक विशिष्ट संख्या है। बिग-ओ विकास के दर के संबंध में संचार करता है कि एन कैसे बदलता है। जोहान: दो तरीकों में एक चर नाम का उपयोग नहीं करना सबसे अच्छा है। हम आम तौर पर या तो कहेंगे O(Y-X), या हम कहेंगे O(Z) where Z=Y-X
मूविंग डक

2
@GregRogers इस तरह का उपयोग करके, हमें एक नया वेक्टर घोषित करने की आवश्यकता है। क्या मूल वेक्टर को बदलने का कोई तरीका है? myVec (पहले, अंतिम) जैसा कुछ? मुझे पता है कि यह गलत है, लेकिन मुझे वास्तव में समाधान की आवश्यकता है क्योंकि मैं अपने कोड में पुनरावृत्ति का उपयोग करना चाहता हूं, और एक ही वेक्टर (हालांकि परिवर्तित) को बार-बार उपयोग करने की आवश्यकता है। धन्यवाद!
ulyssis2

13
सिर्फ क्यों नहीं vector<T> newVec(myVec.begin() + 100000, myVec.begin() + 101000);?

88

बस वेक्टर कंस्ट्रक्टर का उपयोग करें।

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

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

2
ठीक है, मुझे नहीं पता था कि एक मनमाना वेक्टर तत्व से एक पुनरावृत्ति प्राप्त करना सरल था।
19

5
उन सदिश तत्वों का पता लेना एक नगण्य हैक है जो अगर सदिश भंडारण वास्तव में सन्निहित नहीं है तो टूट जाएगा। उपयोग शुरू () + 100000 आदि
j_random_hacker

2
मेरा बुरा, स्पष्ट रूप से मानक गारंटी देता है कि वेक्टर स्टोरेज सन्निहित है। फिर भी, इस तरह के पते के साथ काम करना बुरा है क्योंकि यह निश्चित रूप से यादृच्छिक पहुँच का समर्थन करने वाले सभी कंटेनरों के लिए काम करने की गारंटी नहीं है, जबकि शुरू () +2000 है।
j_random_hacker

33
@j_random_hacker: क्षमा करें असहमत हैं। इस प्रकार की प्रक्रिया का समर्थन करने के लिए std :: वेक्टर के एसटीएल विनिर्देश स्पष्ट रूप से बदल दिए गए थे। इसके अलावा एक पॉइंटर मान्य प्रकार का पुनरावृत्त है। इट्रेटर_ट्रेट्स <>
मार्टिन यॉर्क

6
@ taktak004 नहींं। याद रखें कि operator[]एक संदर्भ देता है। यह केवल उस बिंदु पर है जहां आप संदर्भ पढ़ते या लिखते हैं कि यह एक एक्सेस उल्लंघन बन जाएगा। चूँकि हम न तो करते हैं, बल्कि उस पते को प्राप्त करते हैं, जिसे हमने UB नहीं लगाया है।
मार्टिन यॉर्क

28

std::vector<T>(input_iterator, input_iterator)आपके मामले में foo = std::vector<T>(myVec.begin () + 100000, myVec.begin () + 150000);, उदाहरण के लिए यहां देखें


1
चूंकि एंड्रयू एक नए वेक्टर का निर्माण करने की कोशिश कर रहा है, इसलिए मैं "foo = std :: वेक्टर (..."
ड्रू डॉर्मन

4
हाँ, निश्चित रूप से, लेकिन आप चाहे टाइप करें std :: वेक्टर <int> foo = std :: वेक्टर (...) या std :: वेक्टर <int> foo (...) कोई फर्क नहीं पड़ता।
ऐंटरु

19

इन दिनों, हम spanएस का उपयोग करते हैं ! तो आप लिखेंगे:

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

के रूप में एक ही प्रकार के 1000 तत्वों की अवधि पाने के लिए myvec। या एक और अधिक संक्षिप्त रूप:

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

(लेकिन मुझे यह उतना पसंद नहीं है, क्योंकि प्रत्येक संख्यात्मक तर्क का अर्थ पूरी तरह से स्पष्ट नहीं है; और यह खराब हो जाता है अगर लंबाई और start_pos परिमाण के समान क्रम के हों।)

वैसे भी, याद रखें कि यह प्रतिलिपि नहीं है, यह वेक्टर में डेटा का सिर्फ एक दृश्य है, इसलिए सावधान रहें। यदि आप एक वास्तविक प्रति चाहते हैं, तो आप ऐसा कर सकते हैं:

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

टिप्पणियाँ:

  • gslदिशानिर्देश समर्थन पुस्तकालय के लिए खड़ा है। के बारे में अधिक जानकारी के लिए gsl, देखें: http://www.modernescpp.com/index.php/c-core-guideline-the-guidelines-support-library
  • के एक कार्यान्वयन के लिए gsl, देखें: https://github.com/Microsoft/GSL
  • C ++ 20 का कार्यान्वयन प्रदान करता है span। आप उपयोग करेंगे std::spanऔर #include <span>इसके बजाय #include <gsl/span>
  • स्पैन के बारे में अधिक जानकारी के लिए, देखें: "स्पैन" क्या है और मुझे कब उपयोग करना चाहिए?
  • std::vector एक गज़िलियन कंस्ट्रक्टर हैं, यह एक सुपर-आसान है जिसमें आप उपयोग करने का इरादा नहीं रखते हैं, इसलिए सावधान रहें।

उपयोग करेगा cbeginऔर cendसिर्फ सिद्धांत के लिए;) std::cbeginआदि भी।
JHBonarius

1
@JHBonarius: यह देखते हुए कि कंटेनर की पसंद पर इस कोड को कैसे नहीं हटाया गया है, मुझे नहीं लगता कि कोई विशेष लाभ है; स्वाद की बात है कि मुझे लगता है।
einpoklum

10

यदि दोनों को संशोधित नहीं किया जा रहा है (आइटम जोड़ना / हटाना नहीं है - मौजूदा लोगों को संशोधित करना तब तक ठीक है जब तक आप थ्रेडिंग मुद्दों पर ध्यान देते हैं), आप बस इधर data.begin() + 100000- उधर से गुजर सकते हैं data.begin() + 101000, और दिखावा कर सकते हैं कि वे हैंbegin() और end()छोटे वेक्टर के हैं।

या, चूंकि वेक्टर स्टोरेज सन्निहित होने की गारंटी है, आप बस लगभग 1000 आइटम सरणी पास कर सकते हैं:

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

इन दोनों तकनीकों में निरंतर समय लगता है, लेकिन इसके लिए आवश्यक है कि डेटा की लंबाई न बढ़े, जिससे वास्तविक उत्थान हो।


यह भी अच्छा है यदि आप चाहते हैं कि मूल वेक्टर और सबवेक्टर को जोड़ा जाए।
PyRulez

7

यह चर्चा बहुत पुरानी है, लेकिन सरलतम का उल्लेख अभी तक नहीं किया गया है, सूची-आरंभीकरण के साथ :

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

इसके लिए c ++ 11 या इसके बाद के संस्करण की आवश्यकता होती है।

उदाहरण का उपयोग:

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

परिणाम:

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

आपने उल्लेख नहीं किया कि प्रकार क्या std::vector<...> myVecहै, लेकिन अगर यह एक साधारण प्रकार या संरचना / वर्ग है जिसमें संकेत शामिल नहीं हैं, और आप सबसे अच्छी दक्षता चाहते हैं, तो आप एक डायरेक्ट मेमोरी कॉपी कर सकते हैं (जो मुझे लगता है कि पहले से अधिक तेज़ होगा अन्य जवाब दिए गए)। इस मामले में std::vector<type> myVecकहां के लिए एक सामान्य उदाहरण typeहै int:

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
मुझे आश्चर्य है कि -O3 के साथ, @ Anteru की "कंस्ट्रक्टर का उपयोग std::vector(myVec.begin () + 100000, myVec.begin () + 150000);करना " , इस उत्पाद के लंबे संस्करण का बिल्कुल उसी विधानसभा में नहीं होगा?
सैंडहॉर्न

1
MSVC ++ 2015, उदाहरण के std::vector<>(iter, iter)लिए memmove(), यदि उपयुक्त है (यदि निर्माता तुच्छ है, तो तुच्छ की उपयुक्त परिभाषा के लिए) संकलन करता है ।
पाब्लो एच

1
फोन मत करो memcpy। एक std::copyया एक कंस्ट्रक्टर करें जो एक सीमा (दो पुनरावृत्तियों) को स्वीकार करता है, और संकलक और std.library को memcpyउपयुक्त होने पर कॉल करना होगा ।
बुलेटमेग्नेट


3

जब आप सबवेक्टर का आकार होता है तो आप O (M) प्रदर्शन के साथ STL कॉपी का उपयोग कर सकते हैं ।


Upvoted क्योंकि यह मुझे सही दिशा में इंगित करता है, लेकिन मैं देख सकता हूं कि @LokiAstari का सुझाव है कि यह सही विकल्प नहीं है - चूंकि STL :: copy दो std :: वेक्टर <T> एक ही आकार और प्रकार के सरणियों के साथ काम करता है। यहाँ, ओपी एक नए, छोटे सरणी में एक उप-धारा की प्रतिलिपि बनाना चाहता है जैसा कि यहाँ ओपी के पद में उल्लिखित है: "0 <= X <= Y <= N-1"
एंड्रयू

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

@LokiAstari क्यों नहीं?
क्रिस

2
@ लोकीअस्तरी I इसमें एक संपादन का उल्लेख किया गया था जो सहकर्मी समीक्षा से बच नहीं पाया, जिसने उदाहरण <br/> वेक्टर <T> newvec; std :: copy (myvec.begin () + 10000, myvec.begin () +10100, std :: back_inserter (newvec)); <br/> इस मामले में, आपको पहले गंतव्य बनाने की आवश्यकता नहीं है, लेकिन निश्चित रूप से, प्रत्यक्ष आरंभीकरण अधिक है ... प्रत्यक्ष।
क्रिस

1
@chrisg: इसकी भी दो लाइनें हैं। इसके अतिरिक्त आपको यह सुनिश्चित करने के लिए एक तीसरी पंक्ति को छड़ी करने की आवश्यकता है कि यह कुशल है। newvec.reserve(10100 - 10000);। आईटी निश्चित रूप से एक विकल्प है और तकनीकी रूप से यह काम करेगा। लेकिन दोनों में से आप किसकी सिफारिश करने जा रहे हैं?
मार्टिन

1

एक संग्रह को पेश करने का एकमात्र तरीका जो रैखिक समय नहीं है, वह बहुत आलसी है, जहां परिणामी "वेक्टर" वास्तव में एक उपप्रकार है जो मूल संग्रह को दर्शाता है। उदाहरण के लिए, स्काला की List#subseqविधि निरंतर समय में एक उप-अनुक्रम बनाती है। हालाँकि, यह केवल तभी काम करता है जब संग्रह अपरिवर्तनीय है और यदि अंतर्निहित भाषा खेल कचरा संग्रह है।


c ++ में ऐसा करने का तरीका होगा कि वेक्टर के बजाय X के साथ साझा करने के लिए और फिर SP की नकल करने के लिए साझा करें_ का एक वेक्टर होगा, लेकिन दुर्भाग्य से मुझे नहीं लगता कि तेज है क्योंकि परमाणु ऑपरेशन में SP को शामिल करना शामिल है। या मूल वेक्टर इसके बजाय वेक्टर के एक const साझा_ptr हो सकता है और आप बस इसमें व्यवस्था करने के लिए संदर्भ लेते हैं। .c आपको न तो इसे वेक्टर का एक शेयर्ड_पार्ट बनाने की जरूरत है, लेकिन फिर आपको जीवन भर की समस्याएं हैं ... यह सब मेरे सिर के ऊपर है, गलत हो सकता है ...
NoSenseEtAl

0

केवल दूसरों के लिए यह देर से पोस्ट करना..मुझे लगता है कि पहला कोडर अब तक किया जा चुका है। सरल डेटाटिप्स के लिए किसी भी कॉपी की आवश्यकता नहीं है, बस पुराने सी कोड तरीकों को वापस कर दें।

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

इसके बाद पॉइंटर पी और एक लेन को एक सबवेक्टर की जरूरत है।

नोटलेन होना चाहिए !! len < myVec.size()-start


यह एक प्रति प्रदर्शन नहीं करता है।
ट्रिलियनियन

0

हो सकता है कि GSL लाइब्रेरी में array_view / span एक अच्छा विकल्प हो।

यहाँ भी एक एकल फाइल दिया गया है: array_view


लिंक के साथ कृपया यहाँ उत्तर भी जोड़ें। जैसा कि भविष्य में बाहरी लिंक बदल सकता है
पैंथर

0

तत्वों को एक वेक्टर से दूसरे में आसानी से कॉपी करें
इस उदाहरण में, मैं
` समझने में आसान बनाने के लिए जोड़े के वेक्टर का उपयोग कर रहा हूं

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

'
जैसा कि आप देख सकते हैं कि आप तत्वों को एक वेक्टर से दूसरे में आसानी से कॉपी कर सकते हैं, यदि आप उदाहरण के लिए हम इंडेक्स 10 से 16 तक के तत्वों को कॉपी करना चाहते हैं

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

और यदि आप इंडेक्स 10 के तत्वों को अंत से कुछ इंडेक्स में चाहते हैं, तो उस स्थिति में

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

उम्मीद है कि यह मदद करता है, बस आखिरी मामले में याद रखें v.end()-5 > v.begin()+10


0

फिर भी एक अन्य विकल्प: ए thrust::device_vectorऔर ए के बीच चलते समय उदाहरण के लिए उपयोगी thrust::host_vector, जहां आप कंस्ट्रक्टर का उपयोग नहीं कर सकते।

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

जटिलता O (N) भी होनी चाहिए

आप इसे शीर्ष एवर कोड के साथ जोड़ सकते हैं

vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
std::copy(first, last, std::back_inserter(newVector));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.