▶ [STL] vector 컨테이너
배열을 선언할 때 입력으로 몇 개의 데이터가 들어올 수 있는지 정확히 알지 못할 때가 있다.
표준 라이브러리에 있는 malloc()으로 배열을 동적 할당해본 경험이 있는가?
오늘 vector를 배우고 나면 특별한 경우가 아닌 이상 일일이 동적 할당해줄 필요가 없다.
STL의 vetor는 배열을 동적 할당한 뒤 입력이 너무 많아 할당된 공간을 초과하는 경우 자동으로 사이즈를 늘려주도록 구현되어 있기 때문이다. 이미 구현되어 있는 클래스를 가져다가 사용 방법만 익혀 사용하면 된다.
벡터는 대표적인 시퀀스 컨테이너이다.
컨테이너에 대해 알고싶다면 아래의 링크를 클릭하세요.
link : 컨테이너란?
vector 컨테이너의 자주 사용되는 생성자와 멤버 함수를 살펴보자.
더 다양한 함수들이 있지만 개인적으로 알고리즘 문제를 해결하는데는 이정도만 알아도 충분하다고 생각한다.
# vector 생성자
선언 | 설명 |
vector<?> vt |
빈 컨테이너를 생성한다. |
vector<?> vt(n) |
컨테이너에 n개의 원소를 저장하여 생성한다(default 값 저장) |
vector<?> vt(n,x) |
컨테이너에 n개의 원소를 저장하는데, x의 값을 넣어서 생성 |
vector<?> vt(vt2) |
vt2와 똑같은 원소를 가지는 컨테이너를 생성 |
# 2차원 벡터 선언 및 초기화
선언 | 설명 |
vector< vector<?> > vt(n, vector(n, x)) |
n x n 이차원 벡터를 x로 초기화하여 생성한다. |
# vector 멤버 함수
호출 | 설명 |
vt.push_back(x) |
마지막 원소 뒤에 x를 삽입한다. |
vt.pop_back() |
마지막 원소를 삭제한다. |
vt.clear() |
모든 원소를 삭제한다. |
iter = vt.begin() |
vt의 첫 번째 원소를 가리키는 반복자를 리턴한다. |
iter = vt.end() |
vt의 마지막 원소 다음을 가리키는 반복자를 리턴한다. |
vt.size() |
원소의 개수를 리턴한다. |
n_iter = vt.erase(iter) |
iter가 가리키는 원소를 삭제한다. 그리고 다음 원소를 가리키는 반복자를 리턴한다. |
vt.back() | 마지막 원소를 리턴한다. |
vt.front() | 첫번째 원소를 리턴한다. |
# 원소 접근 방법
vt[idx] | idx번째 원소를 참조한다. (범위에 대한 예외처리 X) |
vt.at(idx) | idx번째 원소를 참조한다. (범위에 대한 예외처리 O) |
반복자에 대해 알고싶다면 아래의 링크를 클릭하세요.
link : 반복자란?
아래의 문제를 해결할 때, 위에 있는 vector 생성자와 멤버 함수를 어떻게 활용하는지 보고 익혀보도록 하자.
▶ 문제
n을 입력받아서 n개의 데이터를 저장할 수 있는 vector를 선언하고
0으로 초기화 시키되 인덱스 0과 5의 배수가 되는 인덱스에 1을 저장해보자.
( 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ... 0 0 0 0 1 이런 식으로 저장하면 된다. )
▷요구 조건 1 : n과 관계 없이 항상 마지막은 1로 끝나도록 구현하라.
n이 7인 경우를 예를 들면,
결과 값이 [ 1 0 0 0 0 1 0 ] 이 아니라
뒤에 값을 더 추가해서 [ 1 0 0 0 0 1 0 0 0 0 1 ] 이 되도록 구현해야한다.
▷요구 조건 2 : 원소들 중 1을 제거한 뒤에 출력하라.
요구 조건 1을 만족시킨 결과가 [ 1 0 0 0 0 1 0 0 0 0 1 ] 이라면,
출력 결과는 [ 0 0 0 0 0 0 0 0 ] 가 되어야 한다.
▶ 설계
vetor를 선언하면서 n개의 공간을 0으로 채우고,
5의 배수가 되는 인덱스에 1을 채우면서 인덱스를 증가시키다가
인덱스가 n이상이 되면 할당되어있는 공간의 마지막 인덱스에서 가장 가까운 1의 인덱스까지 0이 몇 개인지 세어서 문제에서 요구하는 조건을 만족시키도록 값을 채워 넣어 요구 조건 1을 만족시키자.
그다음 iterator(반복자)를 사용해서 첫번째 원소부터 마지막 원소까지 순회하면서 1이 나오는 경우 erase로 제거하여 요구 조건 2를 만족시키자.
▶ 구현
우선 어떤 형태로 함수를 정의할지 선언하고 main문을 작성해보자.
[main.cpp]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | vector<int> meetRequire1(const vector<int>& vt); vector<int> meetRequire2(const vector<int>& vt); void printVt(const vector<int>& vt); int main(){ int n; cin >> n; vector<int> vt(n, 0); vector<int> resVt1 = meetRequire1(vt); vector<int> resVt2 = meetRequire2(resVt1); printVt(resVt2); return 0; } | cs |
line |
설명 |
1 |
meetRequire1()은 vt를 인자로 받아 요구 조건 1을 만족시키는 벡터 컨테이너를 반환하도록 선언했다. |
2 |
meetRequire2()는 vt를 인자로 받아 요구 조건 2을 만족시키는 벡터 컨테이너를 반환하도록 선언했다. |
3 |
vt를 인자로 받아 출력시키는 기능을 하도록 선언했다. |
6~8 | n을 입력받아 n 사이즈의 vector 컨테이너에 0을 넣어서 생성했다. |
9~11 | 요구 조건 1과 2를 만족시키는 resVt2를 반환받아 출력하도록 구현했다. |
이제 함수들을 정의해보자.
[요구 조건 1]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | vector<int> meetRequire1(const vector<int>& vt){ vector<int> retVt(vt); int end = retVt.size(); for(int i=0; i<end; i+=5){ retVt[i] = 1; } if(retVt.back() != 1){ int cnt = 1; while(retVt[end-1-cnt] != 1){ cnt++; } for(int i=0; i<4-cnt; i++) retVt.push_back(0); retVt.push_back(1); } return retVt; } | cs |
line | 설명 |
2 | retVt는 vt의 원소를 복사하여 생성된다. |
3~6 | 인덱스 0과 5의 배수에 1을 저장해주고 있다. |
7 | retVt의 마지막 인덱스가 1이 아닌지 확인한다. |
8~11 | retVt의 마지막 인덱스에서부터 앞으로 보면서 마지막으로 나온 1 뒤에 0이 몇 개 있는지 카운팅한다. |
12~13 | 4-cnt개 만큼 뒤에 0을 더 삽입한다. |
14 | 마지막에 원소 1을 삽입한다. |
[요구 조건 2]
1 2 3 4 5 6 7 8 9 10 11 | vector<int> meetRequire2(const vector<int>& vt){ vector<int> retVt(vt); vector<int>::iterator iter = retVt.begin(); while(iter != retVt.end()){ if(*iter == 1) iter = retVt.erase(iter); else iter++; } return retVt; } | cs |
line | 설명 |
2 | retVt는 vt의 원소를 복사하여 생성된다. |
3 | iter는 첫 번째 원소를 가리키는 반복자를 반환 받는다. |
4~9 | retVt의 첫번째 원소부터 마지막 원소까지 순회하면서 1을 제거한다. |
6 | iter는 삭제된 원소 다음을 가리키는 반복자를 반환 받는다. |
[출력]
1 2 3 4 5 6 | void printVt(const vector<int>& vt){ cout<<"[ "; for(int i=0; i<vt.size(); i++) cout<<vt[i]<<" "; cout<<"]"<<endl; } | cs |
단순히 벡터 vt를 순회하면서 출력하도록 구현했다.
▶ 결과
▶Solution
'■ C++ > STL 컨테이너' 카테고리의 다른 글
[STL 컨테이너] deque 한번에 정리하기 (0) | 2019.12.20 |
---|