2021. 8. 20. 23:44ㆍ알고리즘 문제풀기/코딩
도입
vector
("벡터")는 간단하게 말하면 기능이 몇 개 추가된 배열입니다. 수학에서 말하는 벡터, "기하와 벡터" 할 때의 그 벡터를 떠올리는 분도 있겠지요. 관련이 완전히 없는 것은 아니지만 크게 중요하지는 않아요.
사용 방법
코드 첫 줄에 다음을 적습니다.
#include <vector>
벡터는 배열과 비슷하게 변수처럼 선언합니다. 단 타입명을 꺽쇠로 감싸서 명시해 주어요.
아래 코드는 벡터를 쓰지 않은 코드입니다.
#include <iostream>
using namespace std;
int a[100];
int main() {
cin >> a[0];
cin >> a[1];
cin >> a[12];
cout << (a[1] + a[2] + a[3]) << endl;
return 0;
}
아래 코드는 벡터를 쓰는 코드입니다. 동작은 동일해요.
#include <iostream>
#include <vector>
using namespace std;
vector<int> a(100);
int main() {
cin >> a[0];
cin >> a[1];
cin >> a[12];
cout << (a[1] + a[2] + a[3]) << endl;
return 0;
}
두 코드의 차이가 뭘까요? #include
문이 생겼고, 배열 선언이 vector
선언으로 대체되었어요. 즉 vector<int> a(100);
는 int a[100];
와 비슷한 효과를 낸다는 거지요.
<int>
이렇게 꺽쇠로 감싼 부분이 처음 보면 낯설지도 몰라요. 헤더파일 이름을 감싸는 것과 똑같은 모양이죠. 비슷하게 short
형이나 char
배열도 만들 수 있어요.
#include <vector>
using namespace std;
int main() {
vector<short> vA(5);
vector<char> vB(3);
vA[0] = 3;
vA[1]++;
vA[2] = vA[0] + 5;
vA[3] = vA[1] * vA[2];
vB[0] = 'a';
vB[2] = '\n';
return 0;
}
잠깐! 여기서 뭔가 이상한 걸 눈치채셨나요? 아래 코드를 읽어 보세요.
// ...
int main() {
short vA[5];
char vB[3];
vA[0] = 3;
vA[1]++; // <----- ???
// ...
}
로컬 변수(함수 내에서 선언한 변수)를 초기화하지 않고 사용하면 처음 값을 보장할 수 없지요. 그러니까 이 배열을 사용한 코드에서 vA[1]
의 처음 값은 정해져 있지 않고, ++
연산을 적용한 후의 값도 당연히 알 수 없어요. 그런데 vector
를 이렇게 쓰는 경우엔 일일이 초기화하지 않아도 값이 모두 0임이 보장되어 있어요. 그러니 벡터를 사용한 코드에서 vA[1]++;
의 연산을 거치기 전에는 값이 확실히 0이고 이후에는 1이 돼요.
벡터는 초기값도 설정할 수 있고, 함수의 반환형으로도 쓸 수 있어요.
vector<int> f() {
vector<int> ret(5);
ret[0] = 3;
return ret;
}
vector<int> g() {
vector<int> ret = {1, 2, 3};
return ret;
}
vector<int> h() {
return {1, 2, 3};
}
- 참고로 위의 코드는 C++11 이후의 표준을 준수하는 환경(C++11, C++14, C++17, C++20, ……)에서만 컴파일 가능합니다. 무슨 말인지 모르시겠다면 온라인 저지에서 언어를 선택할 때 C++14 혹은 C++17 등 최신 사양을 선택하시면 돼요.
g()
를 보면 마치 배열을 초기화하는 것 같죠? h()
는 훨씬 간단해서 편리하구요.
인덱스와 크기
벡터의 인덱스는 0에서 (크기)-1까지만 사용할 수 있습니다. 배열과 마찬가지예요. 즉 int a[5];
혹은 vector<int> a(5);
에 대해 a[0]
부터 a[4]
까지의 값은 문제 없이 사용 가능하지만 a[-1]
이나 a[5]
, a[9999]
같은 값에 접근을 시도해서는 안 돼요.
벡터의 크기도 따로 확인할 수 있어요.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {1, 2, 3};
cout << v.size() << endl; /* prints: 3 */
return 0;
}
다만 여기서 또 주의할 점이 있어요. v.size()
의 값은 size_t
타입이고, 이건 환경에 따라 다르지만 보통 unsigned long
타입이에요. long
이 32비트인지 64비트인지가 중요한 게 아니라 unsigned
라는 사실이 중요해요! 그러니까 이런 일이 발생해요.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {1, 2, 3};
cout << (v.size()-4) << endl;
/* prints: 18446744073709551615 */
cout << (v.size() > -4 ? "v.size() > -4" : "v.size() < -4") << endl;
/* prints: v.size() < -4 */
return 0;
}
즉 예상치 못한 곳에서 unsigned underflow가 일어나기 쉽고, 비교나 산술 연산 시에 음수가 unsigned로 변환되어 굉장히 큰 양수로 취급되기 쉬워요. 컴파일러가 경고를 띄워 주기도 하지만, 이런 건 스스로 잡을 수 있어야겠죠?
원소 추가
벡터가 일반 배열에 비해 훨씬 강력한 이유는 바로 원소를 동적으로 추가/삭제할 수 있기 때문이에요. 다음 코드를 볼까요?
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {1, 2, 3};
cout << v[2] << endl; // v = {1, 2, 3}; prints 3
v.push_back(4); // v = {1, 2, 3, 4}
cout << v[3] << endl; // prints 4
v.pop_back(); // v = {1, 2, 3}
cout << v.size() << endl; // prints 3
v.push_back(-5); // v = {1, 2, 3, -5}
cout << v[3] << endl; // prints -5
return 0;
}
push_back(x)
함수로 x
라는 값의 원소를 제일 뒤에 추가할 수 있고, pop_back()
함수로 제일 뒤의 원소를 제거할 수 있어요. 원소의 추가 및 제거에 따라 size()
의 값도 바뀌구요. 이 점이 배열보다 낫죠. 배열을 int a[3];
처럼 선언하고 나면 a[3]
은 영영 접근할 방법이 없는데, 벡터는 크기를 자유자재로 늘릴 수 있어요.
원소 순회
배열의 크기를 알면 배열을 순회할 수 있죠.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {2, 3, 5};
for (int i = 0; i < (int)v.size(); ++i) {
cout << i << ": " << v[i] << endl;
}
return 0;
}
/* prints:
0: 2
1: 3
2: 5
*/
int
변수로 인덱스를 순회할 때에는 위의 소스처럼 배열의 크기를 int
형으로 변환해서 비교해 주는 게 좋아요. 아니면 i
를 unsigned int
형으로 변환해도 되구요.
인덱스를 전혀 안 쓰고 순회하는 방법도 있어요.
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {2, 3, 5};
for (int x : v) {
cout << x << endl;
}
return 0;
}
/* prints:
2
3
5
*/
for
문 안쪽에 보통 쓰던 것처럼 세미콜론(;
)이 없고 콜론(:
)이 있죠. 자바, 파이썬, 자바스크립트 등 다른 많은 언어가 비슷한 문법을 지원해요.
마치며
여기까지가 벡터의 기초적인 사용 방법이에요. 이것 말고도 기능이 많이 있는데 다른 글에서 살펴 보겠습니다.
'알고리즘 문제풀기 > 코딩' 카테고리의 다른 글
PS에서 사용하는 C++ STL - 초급 (pair) (401) | 2021.08.29 |
---|---|
빠르고 편하게 코딩하는 팁 (1) | 2021.08.24 |
PS에서 사용하는 C++ STL - 기초 (0) | 2021.08.20 |
std::sort를 이용한 정렬 (2) | 2015.09.20 |
구조체 (0) | 2015.09.20 |