오늘은 백준 문제를 풀때 굉장히 많이 쓰이는 STL 중 하나인 vector에 대한 정보를 쓰려고 한다. 앞으로 뭐 algorithm, utility, string 등 코테 문제 풀 때 도움이 많이 되는 stl들에 대한 정보를 조금씩 써내려가려고 한다. 근데 일단 많이 쓰이는 함수들 위주로만 쓰려고 하는데, 새로운 것들을 사용할 때마다 계속 추가하면서 수정해나가는 식으로 진행하려고 한다.
먼저 stl vector는 사용하기 위해서
#include <vector>
를 통해 헤더를 include 해줘야 한다.
일단 먼저 이 벡터를 사용하는 이유에 대해 알아야 한다. 배열과 비슷한 기능?을 수행할 수 있는데, 배열같은 경우는 처음에 선언 시에 크기를 반드시 정해줘야 한다. 그러면 배열을 사용해야 되는데 이런 식으로 크기를 지금은 모르고 나중에나 알 수 있는 경우라면? 혹은 계속 원소가 추가돼야 한다면(배열의 크기가 늘어나야 된다면)? 이런 경우는 배열로 하기엔 뭔가 너무 애매하다. 그래서 이럴 때 사용하는 것이 벡터다. 어떤 분들은 배열보단 거의 벡터 위주로만 사용하는 경우도 있더라..
아, 참고로 지금 위에서 설명한 사용 이유라던지, 앞으로 설명할 사용법이라던지..거의 대부분 내 뇌에서 나온 정보들이 많기 때문에 정확하지 않을수도 있고, 다른 중요한 내용들을 많이 빠뜨렸을 수도 있다. 근데 일단 지금까지는 백준 문제를 이 뇌로 풀면서 딱히 문법적으로 문제가 됐던 적은 없어서 그냥 이렇게 적는다. 그래도 차라리 딱딱하게 적는거보단 이렇게 적는게 좀 더 이해가 잘 될수도 있지 않을까 싶다.
이제 대충 사용 이유나 목적에 대해 알았으니 사용법을 알아보자. 먼저 벡터 선언을 하는 방법이다.
vector<type> name;
가장 기본적인 벡터 선언(생성) 방식이다. type은 만들고자 하는 벡터의 자료형(벡터 안에 넣을 요소의 자료형)을 말하고, name은 선언할 벡터의 변수명을 말한다. 선언 시에 벡터를 특정 값으로 초기화해주고 싶다면
vector<type> name(num);
이렇게 해주면, num으로 초기화까지 된다. 그리고 2차원 배열처럼 벡터도 2차원 벡터로 만들 수 있다.
vector<vector<type>> name;
이렇게 하면 2차원 벡터가 만들어지게 된다.
그리고 많이 쓰는 것중 하나가 v.begin()과 v.end()다. v.begin()은 벡터의 시작주소값을 반환해준다. v.end()는 벡터의 끝 주소를 반환해준다. 근데 이때 이 끝이 마지막으로 값이 담겨있는 칸 그 다음칸을 의미한다. 아주 간단하게 그림을 수기로 그려봤다(많이 허접하다).
이거 왜 넣은지도 모를 정도로 참 대충 그렸다. 어쨌든 이런식이고, 비슷하게 v.front()와 v.back()도 있다. 이는 뒤에서 설명한다.
벡터에서 특정 인덱스의 값에 접근하는 것은 배열처럼 []를 통해 접근한다. 예를 들어 두번째 벡터값을 출력하고 싶으면 그냥
vector<int> v;
cout<<v[1]<<endl; // 두번째 벡터값 출력
이런식으로 배열이랑 똑같이 수행해주면 된다. 물론 at라는 친구로도 접근이 되긴 하는데, 효율성이나 예외처리 측면에서 []를 사용하는 것이 더 좋기도 하고, 무엇보다 우리한테 훨씬 더 익숙하니까 그냥 []를 쓰는게 좋아보이나.
그리고 첫 인덱스 값, 즉 v[0]은 v.front()를 이용하면 되고, 마지막 인덱스 값, 즉 v[size-1]은 v.back()으로 접근이 가능하다. front와 back도 아주 대충 그림을 그려봤다.
사실 너무 대충 그려서 뭔 도움이 되나 싶어서 그린걸 후회중이다.
마지막으로 내가 많이 사용했던 주요함수들이다. 먼저 push_back(삽입값)이다. 이름 그대로 뒤에 push하게 된다. 그러니까 back 옆에 값이 삽입되고, back이 한칸 옮겨지는 방식이겠다.
pop_back()은 벡터의 맨 뒤 요소, 그러니까 back 값을 삭제한다. 당연히 back은 한칸 땡겨질 것이다. 그리고 clear()를 통해 벡터를 완전히 비워줄 수도 있다. size도 자연스럽게 0이 된다. empty()는 벡터가 비어있는지 확인해주는 함수다. bool형 값으로 반환해준다. size()는 벡터 크기를 반환한다. 이는 문자열에서의 length와 비슷한 느낌이라고 볼 수 있을 것 같다.
대충 사용 방식을 써보자면
vector<int> v;
v.push_back(1); // 1
v.push_back(2); // 1 2
cout<<v.front()<<endl; // 1 출력
cout<<v.back()<<endl; // 2 출력
cout<<v.size()<<endl; // 2 출력
if(v.empty())
cout<<"Empty!"<<endl; // 출력하지 X
v.pop_back(); // 1
v.clear(); // 완전히 비워짐
이외에도 원하는 위치에 값을 삽입하도록 해주는 v.insert(위치의 주소값, 삽입값), 원하는 위치의 요소를 삭제하도록 해주는 v.emplace(위치의 주소값, 삭제값), 특정 인덱스의 값을 삭제하는 v.erase(인덱스), 벡터끼리 swap 시켜주는 v.swap(벡터) 등도 있다! 근데 지금까지는 풀면서 위에서 쓴 함수들만 사용했던 것 같다.
v.erase()만 살짝 알아보자. 괄호 안에는 인덱스가 들어가는데, 그냥 1, 2 이렇게 들어가는게 아니라, v.begin()+1, v.end()-1 이런식으로 들어간다. 예를 들어 두번째 원소와, 뒤에서 세번째 원소를 지우고 싶다면
v.erase(v.begin()+1);
v.erase(v.begin()-2);
이런식으로 해주면 된다. 당연히 인덱스니까 0부터 시작인 점은 고려해야 된다!
일단 이 정도면 기본적으로 당장 코테 문제에서 사용할 만한 것들은 적은 것 같다. 다음에는 시간이 되면 pair에 대해서도 좀 써보겠다. 앞으로 계속 백준을 풀면서 추가될때마다 이 글을 수정해나가겠다!