아무튼 공부중/C++

[c++] std container1. 시퀀스 컨테이너(Sequence Container)

멍정 2025. 3. 21. 16:09

* 잘못된 내용에 대한 피드백은 언제나 환영입니다. *

 

 

멍하

멍정이에요.

오늘은 STL컨테이너에 대해 정리해보아요.

 

STL 컨테이너란 무엇인가?

표준 라이브러리는 관련 개체 컬렉션을 저장할, 형식이 안전한 다양한 컨테이너를 제공합니다. 컨테이너는 클래스 템플릿입니다. 컨테이너 변수를 선언할 때 컨테이너가 보유할 요소의 형식을 지정합니다. 컨테이너는 이니셜라이저 목록을 사용하여 생성할 수 있습니다. 요소를 추가 및 제거하고 다른 작업을 수행하기 위한 멤버 함수가 있습니다.

https://learn.microsoft.com/ko-kr/cpp/standard-library/stl-containers?view=msvc-170

 

C++ 표준 라이브러리 컨테이너

자세한 정보: C++ 표준 라이브러리 컨테이너

learn.microsoft.com

 

간단하게 생각하면 list, queue 등의 자료구조를 제공한다고 생각하면 될 것 같아요.

컨테이너는 시퀀스 컨테이너, 연관 컨테이너, 컨테이너 어뎁터

이렇게 3가지로 나눌 수 있습니다.

 

시퀀스 컨테이너부터 천천히 알아보아요.

시퀀스 컨테이너(Sequence Container)

시퀀스 컨테이너는 지정된 요소가 삽이된 순서를 유지합니다. = 데이터를 순서대로 저장합니다.

순서를 유지하는 자료구조라고 할 수 있습니다. 

vector container

배열과 비슷하게 사용할 수 있습니다. 필요에 따라 자동적으로 크기가 증가합니다. (= 동적 배열)

임의로 접근 가능하고(= 원하는 위치에서 꺼낼 수 있음), 길이가 매우 유연(= 길이를 직접 설정할 필요가 없음)합니다.

- heap에 저장

 

자주 쓰는 vector 멤버 함수

push_back(val) 끝에 val 추가 v.push_back(10);
pop_back() 마지막 요소 제거 v.pop_back();
insert(pos, val) pos 위치에 val 삽입 v.insert(v.begin() + 1, 20);
erase(pos) pos 위치 요소 제거 v.erase(v.begin() + 2);
clear() 모든 요소 삭제 v.clear();
size() 요소 개수 v.size();
empty() 비어있는지 확인 (비었으면 true) if (v.empty())
front() 첫 번째 요소 반환 v.front();
back() 마지막 요소 반환 v.back();
at(i) i번째 요소 (범위 검사 O) v.at(0);
operator[] i번째 요소 (범위 검사 X) v[0];
begin() 첫 요소 반복자 auto it = v.begin();
end() 마지막 요소 다음 반복자 auto it = v.end();
resize(n) 크기 조절 (늘리면 기본값으로 채움) v.resize(5);
assign(n, val) 크기를 n으로 하고 전부 val로 설정 v.assign(3, 7); // {7, 7, 7}
swap(v2) 다른 벡터와 내용 교환 v.swap(v2);

 

사용 예시

#include <iostream>
#include <vector>

int main() {
    std::vector<int> v;

    v.push_back(1);      // {1}
    v.push_back(2);      // {1, 2}
    v.push_back(3);      // {1, 2, 3}

    v.insert(v.begin() + 1, 99);  // {1, 99, 2, 3}
    v.erase(v.begin());           // {99, 2, 3}

    v.pop_back();                 // {99, 2}

    std::cout << "앞: " << v.front() << ", 뒤: " << v.back() << "\n"; // 앞: 99, 뒤: 2

    v.resize(5);                  // {99, 2, 0, 0, 0} ← 기본값 0으로 채워짐
    v.clear();                    // {}

    if (v.empty()) {
        std::cout << "비었어요!\n";
    }
}

array container

컴파일 시 크기가 고정됩니다.

- stack에 저장

자주 쓰는 array 멤버 함수

 

at(i) i번째 요소 반환 (범위 검사 O) arr.at(2);
operator[] i번째 요소 (범위 검사 X) arr[2];
front() 첫 번째 요소 반환 arr.front();
back() 마지막 요소 반환 arr.back();
data() 내부 배열의 포인터 반환 int* ptr = arr.data();
size() 전체 요소 개수 반환 arr.size();
fill(val) 모든 요소를 val로 초기화 arr.fill(0);
begin() / end() 반복자 반환 std::sort(arr.begin(), arr.end());
swap(other) 두 array 내용 교환 arr1.swap(arr2);

사용 예시

#include <iostream>
#include <array>
#include <algorithm>

int main() {
    std::array<int, 5> arr = {3, 1, 4, 1, 5};

    std::cout << "첫 번째: " << arr.front() << "\n";   // 3
    std::cout << "마지막: " << arr.back() << "\n";    // 5
    std::cout << "세 번째: " << arr.at(2) << "\n";     // 4

    std::sort(arr.begin(), arr.end());  // 배열 정렬

    std::cout << "정렬 후: ";
    for (int x : arr) {
        std::cout << x << " ";
    }
    std::cout << "\n";
}

deque(Double Ended Queue) container

이름 그대로 앞, 뒤에서의 삽입과 삭제를 빠르게 가능합니다. 연속된 메모리에 값을 저장하는 vector와 다르게 분산되어 여러 개의 메모리 블록으로 구성되어 있습니다. vector와 동일하게 크기를 동적으로 조절할 수 있습니다.

- heap에 저장

 

자주 쓰는 array 멤버 함수

push_back(val) 뒤에 val 추가 dq.push_back(10);
push_front(val) 앞에 val 추가 dq.push_front(20);
pop_back() 뒤에서 요소 제거 dq.pop_back();
pop_front() 앞에서 요소 제거 dq.pop_front();
front() 앞 요소 반환 dq.front();
back() 뒤 요소 반환 dq.back();
at(i) i번째 요소 (범위 검사 O) dq.at(1);
operator[] i번째 요소 (범위 검사 X) dq[1];
size() 요소 개수 dq.size();
clear() 전체 요소 제거 dq.clear();
insert(pos, val) 특정 위치에 삽입 dq.insert(dq.begin()+2, 50);
erase(pos) 특정 위치 제거 dq.erase(dq.begin()+1);
begin() / end() 반복자 std::sort(dq.begin(), dq.end());

사용 예시

#include <iostream>
#include <deque>

int main() {
    std::deque<int> dq;

    dq.push_back(1);       // {1}
    dq.push_front(2);      // {2, 1}
    dq.push_back(3);       // {2, 1, 3}

    std::cout << "앞: " << dq.front() << "\n"; // 2
    std::cout << "뒤: " << dq.back() << "\n";  // 3

    dq.pop_front();        // {1, 3}
    dq.pop_back();         // {1}

    dq.push_back(4);       // {1, 4}
    dq.insert(dq.begin() + 1, 9); // {1, 9, 4}

    for (int x : dq) {
        std::cout << x << " ";
    }
    std::cout << "\n"; // 출력: 1 9 4
}

vector vs deque

항목 vector deque
메모리 구조 연속된 메모리 블록 불연속적인 블록 (블록 단위로 나눠 저장)
임의 접근 ([ ]) 매우 빠름 (캐시 친화적) 빠름 (하지만 vector보단 살짝 느림)
뒤 삽입/삭제 (push_back/pop_back) 매우 빠름 매우 빠름
앞 삽입/삭제 (push_front/pop_front) 매우 느림 (모든 요소 이동 필요) 빠름 (구조상 효율적)
중간 삽입/삭제 느림 (이동 발생) 비슷하게 느림
메모리 재할당 크기 늘어날 때 전체 복사 발생 블록 단위라 더 유연
캐시 효율성 좋음 (연속 메모리) 약간 낮음

-> vector랑 deque는 거의 비슷하기 때문에 무난하게 vector도 괜찮지만 앞 뒤 삽입이 많은 구조라면 deque도 좋습니다.


list container

이중 연결 리스트의 형태로 양방향에서 빠르게 접근할 수 있지만, at, [ ]등의 임의 접근은 불가능합니다.

- heap에 저장

자주 쓰는 list 멤버 함수

push_back(val) 뒤에 추가 lst.push_back(10);
push_front(val) 앞에 추가 lst.push_front(5);
pop_back() 뒤에서 제거 lst.pop_back();
pop_front() 앞에서 제거 lst.pop_front();
insert(pos, val) 위치에 삽입 lst.insert(it, 3);
erase(pos) 위치 제거 lst.erase(it);
remove(val) 해당 값 전부 제거 lst.remove(10);
clear() 전체 삭제 lst.clear();
size() 요소 개수 lst.size();
empty() 비어 있는지 lst.empty();
front() / back() 첫/마지막 요소 반환 lst.front();
begin() / end() 반복자 반환 auto it = lst.begin();
sort() 정렬 lst.sort();
unique() 중복 제거 (연속된 값만) lst.unique();
merge(other) 정렬된 리스트끼리 병합 lst.merge(lst2);
reverse() 순서 뒤집기 lst.reverse();

사용 예시

#include <iostream>
#include <list>

int main() {
    std::list<int> lst = {3, 1, 4, 1, 5};

    lst.push_front(9);   // 앞에 추가
    lst.push_back(2);    // 뒤에 추가

    lst.sort();          // 정렬
    lst.unique();        // 중복 제거 (연속된 것만)

    for (int x : lst) {
        std::cout << x << " ";
    }
    std::cout << "\n";
}

forward_list container

단일 연결 리스트의 형태로 한 방향으로만 연결되어 메모리 효율이 좋습니다.

- heap에 저장

자주 쓰는 forward_list 멤버 함수

push_front(val) 앞에 요소 추가 fl.push_front(10);
pop_front() 앞 요소 제거 fl.pop_front();
insert_after(pos, val) 특정 위치 다음에 삽입 fl.insert_after(it, 5);
erase_after(pos) 특정 위치 다음 요소 삭제 fl.erase_after(it);
front() 첫 번째 요소 반환 fl.front();
clear() 전체 삭제 fl.clear();
empty() 비어있는지 확인 fl.empty();
before_begin() 맨 앞 전 위치 반환 (삽입용) fl.insert_after(fl.before_begin(), 1);
begin() / end() 반복자 반환 for (auto it = fl.begin(); it != fl.end(); ++it)
sort() 정렬 fl.sort();
unique() 연속된 중복 제거 fl.unique();
remove(val) 특정 값 제거 fl.remove(3);
reverse() 뒤집기 fl.reverse();
merge(other) 정렬된 리스트 병합 fl.merge(other);

사용 예시

#include <iostream>
#include <forward_list>

int main() {
    std::forward_list<int> fl = {3, 1, 4, 1, 5};

    fl.push_front(9);                // {9, 3, 1, 4, 1, 5}
    fl.sort();                       // {1, 1, 3, 4, 5, 9}
    fl.unique();                     // {1, 3, 4, 5, 9}
    fl.remove(4);                    // {1, 3, 5, 9}
    fl.reverse();                    // {9, 5, 3, 1}

    for (int x : fl) {
        std::cout << x << " ";
    }
    std::cout << "\n";
}

시퀀스 컨테이너 공통 함수

empty() 비어 있는지 확인 (true / false) 모든 컨테이너 공통
size() 요소 개수 반환 forward_list는 상대적으로 느림
front() 첫 번째 요소 반환 없음 → 예외 발생 위험 있음
back() 마지막 요소 반환 forward_list는 ❌ 없음
clear() 모든 요소 삭제 공통
begin() / end() 반복자 반환 (for-each 가능) 공통
insert(pos, val) pos 위치에 삽입 forward_list는 insert_after()
erase(pos) pos 위치 요소 삭제 forward_list는 erase_after()