백준을 풀던 도중 같은 로직을 사용하였는데도 불구하고, list를 썼을 땐 통과가 되는데 vector를 이용해 구현을 하면 시간 초과가 나는 걸 발견했다. 이런 문제는 꽤나 많이 겪어왔는데, 머리 속에서는 정리가 안 되었다는 생각이 들었다. 그럼 정확히 언제 무엇을 써야 효율적인지 정리해보겠다. 2학년 자료구조 시간에 배운 것 같은데 제대로 기억이 안난다.^^
(아래의 내용은 챗 지피티와 함께 합니다..)
std::stack
컨테이너 어댑터를 사용하여 구현할 수 있습니다.std::queue
컨테이너 어댑터를 사용하여 구현할 수 있습니다.std::deque
컨테이너를 사용하여 구현할 수 있습니다.std::list
컨테이너를 사용하여 구현할 수 있습니다.std::vector
컨테이너를 사용하여 구현할 수 있습니다.std::map
컨테이너를 사용하여 구현할 수 있습니다.std::set
컨테이너를 사용하여 구현할 수 있습니다.이러한 자료구조들은 각각의 특징과 사용 목적에 따라 적합한 상황이 다릅니다. 예를 들어, 스택은 함수 호출과 관련된 임시 데이터 저장에 사용되고, 큐는 작업 대기열 등 순차적인 처리가 필요한 상황에 사용될 수 있습니다. 덱은 양쪽 끝에서의 삽입과 삭제가 필요한 경우에 유용하며, 리스트는 중간 삽입과 삭제가 빈번한 경우에 유용합니다. 벡터는 순차적인 접근과 빠른 임의 접근이 필요한 경우에 사용되며, 맵은 키-값 쌍을 검색하기 위해 사용됩니다. 세트는 중복을 제거하고 유일한 값만을 유지해야 할 때 사용됩니다.
자료구조의 선택은 주어진 문제의 특성과 요구사항에 따라 달라지므로, 문제를 분석하고 필요한 기능과 성능을 고려하여 적절한 자료구조를 선택하는 것이 중요합니다.
위의 내용을 통해서 왜 vector를 사용했을 때 timeover가 발생했는지 알 수 있었다. vector의 경우 임의 위치에서의 삽입과 삭제가 느리기 때문에 임의 위치 삽입, 삭제를 사용할 땐 list가 적절한 자료구조라는 것이다. vector를 애용하는 나로써는 잘 알아두어야 할 특성인 것 같다. 참고로 접근 자체는 빠르다고 하니 헷갈리지 말자!
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<char> v;
auto v_idx = v.begin(); // v가 비어있을 경우에도 v_idx를 초기화할 수 있도록 수정
int n;
string s;
cin >> n;
while (n--) {
v.clear();
v_idx = v.begin();
cin >> s;
for (int i = 0; i < s.length(); i++) {
if (s[i] == '<') {
if (v.begin() != v_idx) {
v_idx--;
}
} else if (s[i] == '>') {
if (v.end() != v_idx) {
v_idx++;
}
} else if (s[i] == '-') {
if (v_idx != v.begin()) {
v_idx = v.erase(--v_idx);
}
} else {
v_idx = v.insert(v_idx, s[i]) + 1;
}
}
for (v_idx = v.begin(); v_idx != v.end(); v_idx++) cout << *v_idx;
cout << '\n';
}
}
#include <iostream>
#include <list>
using namespace std;
int main() {
list<char> v;
auto v_idx = v.begin(); // v가 비어있을 경우에도 v_idx를 초기화할 수 있도록 수정
int n;
string s;
cin >> n;
while (n--) {
v.clear();
v_idx = v.begin();
cin >> s;
for (int i = 0; i < s.length(); i++) {
if (s[i] == '<') {
if (v.begin() != v_idx) {
v_idx--;
}
} else if (s[i] == '>') {
if (v.end() != v_idx) {
v_idx++;
}
} else if (s[i] == '-') {
if (v_idx != v.begin()) {
v_idx = v.erase(--v_idx);
}
} else {
v.insert(v_idx, s[i]);
}
}
for (v_idx = v.begin(); v_idx != v.end(); v_idx++) cout << *v_idx;
cout << '\n';
}
}
두개의 코드를 보면 vector -> list로만 바꿔주었다는 것을 알 수 있다.
그러나 위 로직에서는 iterator로 접근해서 삭제와 삽입하는 연산이 잦게 일어나기 때문에 vector 사용 시 시간 초과가 된다.