문제풀이(Problem Solving)/C++ 문제풀이에 유용한 것들

STL 벡터, vector 사용법 [C++]

게임이 더 좋아 2021. 5. 22. 20:46
반응형
728x170

 

메모리 heap에 생성되며 동적할당이 가능하여 동적 배열을 대체할 수 있는

std::<vector> 를 알아보자

 


 

C++ STL 에서 컨테이너는 크게 두 가지 종류가 있다.

배열 처럼 객체들을 순차적으로 보관하는 시퀀스 컨테이너 (sequence container)

키를 바탕으로 대응되는 값을 찾아주는 연관 컨테이너 (associative container) 가 있다.

 

우리가 알아볼 벡터는 시퀀스 컨테이너다.

 

벡터에는 원소들이 메모리 상에서 실제로 순차적으로 저장되어 있고

따라서 임의의 위치에 있는 원소를 접근하는 것을 매우 빠르게 수행할 수 있다.


하지만 벡터가 만능인줄 알고 있는 사람들도 있지만 그렇지 않다.

맨 뒤에 원소를 추가하거나 제거하는 것은 빠르지만

임의의 위치에 원소를 추가하거나 제거하는 것은 O(n) 으로 느리다.

 

왜냐하면 새로운 원소를 추가하거나 뺄 경우 그 뒤에 오는 원소들을 한 칸 씩 이동시켜 주어야만 하기 때문이다.

따라서 이는 n 번의 복사가 필요로 한다.

 

맨 뒤가 아닌 임의의 장소에서 데이터를 추가하거나 제거하는 작업이 많은 일일 경우 vector 를 사용하면 느려질 것이다.

 

한마디로 말하면 탐색이 가능한.. 스택이랄까??

탐색이 가능하니까 삭제도 가능하고

 


 

벡터의 선언을 어떻게 하는지 보자.

 vector<자료형> 변수명 백터 생성 
 vector<자료형> 변수명(숫자) 숫자만큼 백터 생성 후 0으로 초기화 
 vector<자료형> 변수명 = { 변수1, 변수2, 변수3... } 백터 생성 후 오른쪽 변수 값으로 초기화 
 vector<자료형> 변수명[] = {{변수1, 변수2}, {변수3, 변수4}}  백터 배열(2차원 백터)선언 및 초기화(열은 고정, 행은 가변)
 vector<vector<자료형> 변수명  2차원 백터 생성(열과 행 모두 가변)
 vector<자료형>변수명.assign(범위, 초기화할 값)  백터의 범위 내에서 해당 값으로 초기화

 

 


 

 

또한 벡터에서는

Iterator를 생성할 수 있는데

 v.begin()  백터 시작점의 주소 값 반환
 v.end()   백터 (끝부분 + 1) 주소값 반환
 v.rbegin() (reverse begin)   백터의 끝 지점을 시작점으로 반환 
 v.rend() (reverse end)  백터의 (시작 + 1) 지점을 끝 부분으로 반환 

 

그림

 

begin() 함수는 vector 의 첫번째 원소를 가리키는 반복자를 반환한다.

end() 의 경우 vector 의 마지막 원소 한 칸 뒤를 가리키는 반복자를 반환한다.

 

이러한 것을 이용하여 빈 벡터를 표현할 수 있다.

** 그래서 begin() == end() 라면 원소가 없는 벡터를 가리킬 것이다.

 

 

 

이러한 이터레이터는 어떻게 쓰이느냐??

 

vector<int> v = { 1, 2, 3, 4 };
 
    for_each(v.begin(), v.end(), [&](int& n){
        cout << n << endl;        //output : 1 2 3 4
    });
 
    for_each(v.rbegin(), v.rend(), [&](int& n) {
        cout << n << endl;        //output : 4 3 2 1
    });

 

이렇게도 쓰일 수 있고

vector<int>::iterator itor = v.begin();
 
    for (; itor != v.end(); itor++)
        cout << *itor << endl;        //output : 1 2 3 4
 
 
    vector<int>::reverse_iterator itor2 = v.rbegin();
 
    for (; itor2 != v.rend(); itor2++)
        cout << *itor2 << endl;        //output : 4 3 2 1

 

for, for_each 둘 다 쓰는 형태가 조금 다르지만 가능하다.

 

 

 

 


 

 

그 외 관련 메서드,함수

 v.at(i)   백터의 i번째 요소 접근 (범위 검사함)
 v.[i] (operator [])   백터의 i번째 요소 접근 (범위 검사 안함) 
 v.front()   백터의 첫번째 요소 접근 
 v.back()   백터의 마지막 요소 접근 
 v.reserve(숫자)  백터의 크기 설정 
 v.shrink_to_fit()  capacity의 크기를 백터의 실제 크기에 맞춤 
 v.push_back()   백터의 마지막 부분에 새로운 요소 추가 
 v.pop_back()  백터의 마지막 부분 제거 
 v.insert(삽입할 위치의 주소 값, 변수 값)  사용자가 원하는 위치에 요소 삽입 
 v.emplace(삽입할 위치의 주소 값, 변수 값)    사용자가 원하는 위치에 요소 삽입(move로 인해 복사생성자 X) 
 v.emplace_back()  백터의 마지막 부분에 새로운 요소 추가(move로 인해 복사생성자 X) 
 v.erase(삭제할 위치) or v.erase(시작위치, 끝위치)-iter  사용자가 원하는 index값의 요소를 지운다.
 v.clear()  백터의 모든 요소를 지운다.(return size = 0)
 v.resize(수정 값)  백터의 사이즈를 조정한다.(범위 초과시 0으로 초기화) 
 v.swap(백터 변수)  백터와 백터를 스왑한다. 
 v.max_size()  백터가 system에서 만들어 질 수 있는 최대 크기 반환 
 v.capacity()  heap에 할당된 백터의 실제크기(최대크기) 반환 
 v.size()   백터의 크기 반환 
 v.empty()  백터가 빈공간이면 true, 값이 있다면 false 

 

 

*** vector에서 초기화 시에  '=' 를 사용하면 deep copy가 일어나므로

vector<int> v1;

v1 = v 라고 선언하고

v1을 수정하더라도 v는 바뀌지 않는다.

 

또한 벡터를 순회할 때 이런 식으로도 쓴다. (바킹독님이 잘 정리해주셨다)

 

1번에서 

int e : v1이라고 하면 복사된 값이 e에 들어가고

int& e : v1이라고 하면 원본이 e에 들어간다.

어떻게 선언하느냐에 따라 원본이 수정되는지 복사하는지가 구분된다.

 

728x90
반응형
그리드형