메모리 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에 들어간다.
어떻게 선언하느냐에 따라 원본이 수정되는지 복사하는지가 구분된다.
'문제풀이(Problem Solving) > C++ 문제풀이에 유용한 것들' 카테고리의 다른 글
vector에서 iterator 활용, 벡터 역순 (0) | 2021.06.07 |
---|---|
문자열 <- >정수 변환 string to int, int to string (0) | 2021.06.07 |
Deque, 덱 , 자료구조 [CPP] (0) | 2021.04.23 |
Stack, 스택 , 자료구조 [CPP] (0) | 2021.04.22 |
Queue, 큐 , 자료구조 [CPP] (0) | 2021.04.22 |