벡터에서 서브 벡터를 추출하는 가장 좋은 방법은?


295

내가 size std::vector()라고 가정 해보십시오 . 0 <= X <= Y <= N-1 인 요소 X부터 Y까지의 복사본으로 구성된 새 벡터를 구성하는 가장 간단한 방법은 무엇입니까? 예를 들어 size의 벡터를 통해 통과 합니다 .myVecNmyVec [100000]myVec [100999]150000

벡터로 효율적으로 수행 할 수없는 경우 대신 사용해야하는 다른 STL 데이터 유형이 있습니까?


7
하위 벡터를 추출하고 싶다고 말하지만 실제로 원하는 것은 하위 벡터에 대한보기 / 액세스입니다. 차이가 복사되지 않는다는 점이 다릅니다-구식 C ++은 시작 포인터와 끝 포인터를 사용하는 것입니다. std :: vector의 mem이 연속적이라는 사실을 감안할 때 포인터를 사용하여 반복하여 복사를 피할 수 있어야하지만 복사가 마음에 들지 않으면 이전 벡터의 범위로 새 벡터를 초기화하십시오. 벡터
serup

c ++ 11부터 .data () ( cplusplus.com/reference/vector/vector/data )가 있습니다. 그러나 포인터를 사용하는 것은 stl 컨테이너 내에서 권장하지 않습니다. stackoverflow.com/questions/31663770/…
David Tóth

답변:


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 (NX)보다 작거나 같은 O (YX)입니다 (예 : 훨씬 작음)
orip

74
@orip 음, 결국 O (N)입니다.
Johann Gerell

55
@GregRogers : N이 특정 숫자 인 big-O 표기법을 사용하는 것은 의미가 없습니다. Big-O는 N이 어떻게 변화하는지와 관련하여 성장률을 알려줍니다. Johann : 두 가지 방법으로 하나의 변수 이름을 사용하지 않는 것이 가장 좋습니다. 우리는 보통이라고 말하거나 O(Y-X)이라고 말합니다 O(Z) where Z=Y-X.
Mooing Duck

2
@GregRogers이 방법을 사용하여 새로운 벡터를 선언해야합니다. 원래 벡터를 변경하는 방법이 있습니까? myVec (처음, 마지막)과 같은 것? 나는 이것이 잘못되었다는 것을 알고 있지만, 코드에서 재귀를 사용하고 같은 벡터를 반복적으로 사용해야하므로 솔루션이 정말로 필요합니다 (변경되었지만). 감사!
ulyssis2

13
왜 안돼 vector<T> newVec(myVec.begin() + 100000, myVec.begin() + 101000);?
aquirdturtle

88

벡터 생성자를 사용하십시오.

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

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

2
자, 임의의 벡터 요소에서 반복자를 얻는 것이 그렇게 간단하다는 것을 알지 못했습니다.
An̲̳̳ 님이

5
이러한 벡터 요소의 주소를 가져 오는 것은 벡터 스토리지가 실제로 연속적이지 않은 경우 중단 될 수있는 이식 불가능한 핵입니다. begin () + 100000 등을 사용하십시오.
j_random_hacker

2
나쁘고, 분명히 표준은 벡터 저장이 연속적임을 보장합니다. 그럼에도 불구하고 begin () + 100000은 임의 액세스를 지원하는 모든 컨테이너에서 작동하지 않을 수 있으므로 이와 같은 주소로 작업하는 것은 좋지 않습니다.
j_random_hacker

33
@j_random_hacker : 동의하지 않아야합니다. std :: vector에 대한 STL 스펙이이 유형의 프로 시저를 지원하도록 명시 적으로 변경되었습니다. 또한 포인터는 유효한 반복자 유형입니다. iterator_traits <>를 찾으십시오
Martin York

6
tak operator[]참조 를 반환 한다는 것을 기억하십시오 . 액세스 위반이 될 수있는 참조를 읽거나 쓰는 시점에만 있습니다. 우리는 주소를 얻지 않고 대신 얻었으므로 UB를 호출하지 않았습니다.
Martin York

28

std::vector<T>(input_iterator, input_iterator)귀하의 경우 foo = std::vector<T>(myVec.begin () + 100000, myVec.begin () + 150000);예를 들어 여기를 참조 하십시오


1
Andrew가 새로운 벡터를 만들려고하므로 "foo = std :: vector (..."로 복사하는 대신 "std :: vector foo (..."를 권장합니다.
Drew Dormann

4
예, 물론 std :: vector <int> foo = std :: vector (...) 또는 std :: vector <int> foo (...)를 입력하는지 여부는 중요하지 않습니다.
Anteru

19

요즘에는 spans 를 사용 합니다! 그래서 당신은 쓸 것입니다 :

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

노트:


원칙을 위해 사용 cbegin하고 cend;) std::cbegin등도.
JHBonarius 2016 년

1
@JHBonarius : 컨테이너 선택시이 코드가 어떻게 템플릿 화되어 있지 않은지 살펴보면 특별한 이점이 없습니다. 내가 생각하는 맛의 문제.
einpoklum 2016 년

10

모두 수정하지 않을 경우 (항목이 추가 / 삭제 - 기존 수정하는만큼 당신이 스레딩 문제에주의를 지불로 괜찮습니다)을, 당신은 단순히 주변에 통과 할 수 data.begin() + 100000data.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위치 는 다음과 같습니다 .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
@Anteru의 "using constructor" std::vector(myVec.begin () + 100000, myVec.begin () + 150000);인 -O3을 사용 하면이 생산물의 더 긴 버전이 정확히 동일한 어셈블리로 만들어지지 않을지 궁금합니다 .
sandthorn

1
MSVC ++ 2,015, 예를 들면, 컴파일 std::vector<>(iter, iter)memmove(), 적절한 경우 (사소한 생성자의 적절한 정의는 사소한 경우).
Pablo H

1
전화하지 마십시오 memcpy. DO가 std::copy범위 (두 반복자를) 수락하거나 생성자 및 컴파일러와 std.library는 전화를 공모 할 memcpy때 적합합니다.
Bulletmagnet

4

당신은 그냥 사용할 수 있습니다 insert

vector<type> myVec { n_elements };

vector<type> newVec;

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

3

M이 서브 벡터의 크기 인 경우 O (M) 성능과 함께 STL 복사 를 사용할 수 있습니다 .


STL :: copy가 동일한 크기와 유형의 두 std :: vector <T> 배열과 함께 작동하기 때문에 @LokiAstari가 올바른 선택이 아니라고 제안하는 이유를 알 수 있습니다. 여기서 OP는 OP의 게시물에 설명 된대로 하위 섹션을 더 작은 새 배열로 복사하려고합니다. "0 <= X <= Y <= N-1"
Andrew

@Andrew, std :: copy 및 std :: back_inserter를 사용하는 예제 참조
chrisg

@LokiAstari 왜 안돼?
chrisg

2
@LokiAstari 필자는 피어 리뷰에서 살아남지 못한 편집 ​​내용을 언급했습니다. <br/> vector <T> newvec; std :: copy (myvec.begin () + 10000, myvec.begin () +10100, std :: back_inserter (newvec)); <br/>이 경우 먼저 대상을 빌드 할 필요는 없지만 직접 초기화는 더 직접적입니다.
chrisg

1
@ chrisg : 또한 두 줄입니다. 또한 효율성을 높이려면 세 번째 줄을 붙여야합니다. newvec.reserve(10100 - 10000);. IT는 분명히 옵션이며 기술적으로 작동합니다. 그러나 두 가지 중에서 추천 할 것입니까?
마틴 요크

1

선형 시간이 아닌 컬렉션을 투영하는 유일한 방법은 느리게 만드는 것입니다. 결과 "벡터"는 실제로 원래 컬렉션에 위임하는 하위 유형입니다. 예를 들어 Scala의 List#subseq방법은 일정한 시간에 하위 시퀀스를 만듭니다. 그러나 이것은 컬렉션이 변경 불가능하고 기본 언어가 스포츠 가비지 수집 인 경우에만 작동합니다.


C ++ 방식에서는 X의 벡터 대신 shared_ptr의 벡터를 X로 만든 다음 SP를 복사하는 것이 좋지만 불행히도 원자 연산이 cpying SP와 관련되어 있기 때문에 더 빠르다고 생각하지 않습니다. 또는 원래 벡터는 대신 const const_ptr 벡터의 const가 될 수 있으며 하위 범위를 참조하면됩니다. ofc 당신은 그것을 벡터의 shared_ptr로 만들 필요는 없지만 평생 문제가 있습니다 ...이 모든 것이 내 머리 꼭대기에서 벗어
났을

0

다른 사람들을 위해 이것을 늦게 게시합니다. 나는 첫 번째 코더가 지금 끝날 것이라고 확신합니다. 간단한 데이터 유형의 경우 복사가 필요하지 않으며 오래된 C 코드 메소드로 되돌립니다.

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

그런 다음 포인터 p와 len을 서브 벡터가 필요한 모든 것에 전달하십시오.

notelen은 있어야합니다! len < myVec.size()-start


이것은 복사를 수행하지 않습니다.
Trilarion

0

GSL 라이브러리 의 array_view / span 이 좋은 옵션 일 수 있습니다.

여기에 하나의 파일 구현도 : array_view .


링크와 함께 여기에 답변을 추가하십시오. 향후 외부 링크가 변경 될 수 있음
Panther

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

또 다른 옵션 : 예를 들어 생성자를 사용할 수없는 a thrust::device_vector와 a 사이를 이동할 때 유용합니다 thrust::host_vector.

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

복잡성이어야 함 O (N)

이것을 상위 Anwer 코드와 결합 할 수 있습니다

vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
std::copy(first, last, std::back_inserter(newVector));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.