Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++ STL 완전 정복(3): set과 unordered_set의 차이점 한눈에 보기

C++ STL에는 다양한 연관 컨테이너(Associative Container)가 존재하는데, 그중 가장 많이 사용되는 것이 바로 setunordered_set입니다. 두 컨테이너는 이름이 비슷해 헷갈리기 쉽지만, 내부 동작 방식과 성능 특성이 크게 다릅니다. 이번 글에서는 set과 unordered_set의 개념을 살펴보고, 각각을 언제 사용해야 하는지 그 차이점까지 자세히 알아보겠습니다.

set이란 무엇인가?

set은 정렬된(sorted) 고유한(unique) Key 타입 객체들을 저장하는 연관 컨테이너입니다. 각 요소는 오직 한 번만 나타날 수 있으므로 중복 값은 허용되지 않습니다.

흥미로운 점은, 사용자가 임의의 순서로 요소를 삽입하더라도 set은 항상 정렬된 데이터를 반환한다는 것입니다. 즉, set 내부에는 데이터를 정렬하기 위한 로직이 이미 구현되어 있으며, 이러한 정렬 과정은 사용자에게 추상화되어 숨겨져 있습니다.

set을 사용하면 좋은 경우

  • 정렬된 데이터가 필요할 때
  • 중복 값을 허용하지 않고 유일한 데이터만 필요할 때
  • 해시 테이블(Hash Table) 대신 이진 탐색 트리(Binary Search Tree) 기반 구조를 활용하고 싶을 때
  • 탐색 시간이 크게 문제되지 않는 상황일 때 — set의 탐색 복잡도는 O(log n)입니다.

set 동작 예시

입력:

set = {2, 1, 5, 6, 9, 3, 2}

출력:

1, 2, 3, 5, 6, 9

참고: 값들이 무작위 순서로 삽입되었지만, set이 자동으로 정렬해 주며 중복된 값(2)도 제거됩니다.

set 예제 코드

#include <iostream>
#include <set>
using namespace std;

int main() {
    // 배열 생성
    int arr[] = {2, 1, 5, 6, 9, 3, 2};
    int size = sizeof(arr) / sizeof(arr[0]);

    // set 선언
    set<int> SET;

    // insert() 함수로 배열의 요소를 set에 삽입
    for (int i = 0; i < size; i++) {
        SET.insert(arr[i]);
    }

    set<int>::iterator it;
    cout << "set에 저장된 값들: ";
    for (it = SET.begin(); it != SET.end(); it++) {
        cout << *it << " ";
    }
}

실행 결과

set에 저장된 값들: 1 2 3 5 6 9

배열에 담긴 값들이 무작위 순서였음에도 불구하고, 출력 결과가 오름차순으로 정렬되어 있는 것을 확인할 수 있습니다.

unordered_set이란 무엇인가?

unordered_set 역시 연관 컨테이너의 일종으로, 무작위 순서로 삽입된 비정렬(unordered) 데이터 집합을 저장합니다. 마찬가지로 각 요소는 한 번만 존재할 수 있어 중복은 허용되지 않습니다.

사용자가 어떤 순서로 요소를 삽입하든, unordered_set은 데이터를 특정 순서 없이 반환합니다. 내부적으로 해시 테이블을 사용하기 때문에 요소의 저장 위치는 해시 함수의 결과에 따라 결정됩니다.

unordered_set을 사용하면 좋은 경우

  • 정렬된 데이터가 필요 없고, 비정렬 형태의 데이터로 충분할 때
  • 중복 값을 허용하지 않고 유일한 데이터만 필요할 때
  • 이진 탐색 트리 대신 해시 테이블(Hash Table) 기반 구조를 활용하고 싶을 때
  • 더 빠른 탐색 속도가 필요할 때 — 평균적으로 O(1), 최악의 경우 O(n)의 시간 복잡도를 가집니다.

unordered_set 동작 예시

입력:

set = {2, 1, 5, 6, 9, 3, 2}

출력:

3, 9, 6, 5, 2

입력 순서나 값의 크기와 무관하게, 해시 테이블의 내부 구조에 따라 순서가 결정됨을 알 수 있습니다.

unordered_set 예제 코드

#include <iostream>
#include <unordered_set>
using namespace std;

int main() {
    int arr[] = {2, 1, 5, 6, 9, 3, 2};
    int size = sizeof(arr) / sizeof(arr[0]);

    unordered_set<int> U_SET;

    // insert() 함수로 배열의 요소를 unordered_set에 삽입
    for (int i = 0; i < size; i++) {
        U_SET.insert(arr[i]);
    }

    unordered_set<int>::iterator it;
    cout << "unordered_set에 저장된 값들: ";
    for (it = U_SET.begin(); it != U_SET.end(); it++) {
        cout << *it << " ";
    }
}

실행 결과

unordered_set에 저장된 값들: 3 6 5 9 2 1

출력 결과가 입력 순서와도, 값의 크기 순서와도 일치하지 않습니다. 이는 unordered_set이 해시 테이블 기반으로 동작하기 때문이며, 실행 환경에 따라 출력 순서는 달라질 수 있습니다.

set vs unordered_set 핵심 차이 정리

구분setunordered_set
내부 구현이진 탐색 트리 (레드-블랙 트리)해시 테이블
데이터 정렬 여부항상 정렬된 상태 유지정렬되지 않음
탐색 시간 복잡도O(log n)평균 O(1), 최악 O(n)
중복 허용불가불가
헤더 파일<set><unordered_set>

마무리

정리하자면, 정렬된 순회나 순서가 중요한 경우에는 set을, 탐색 속도가 최우선이고 순서가 중요하지 않은 경우에는 unordered_set을 선택하는 것이 좋습니다. 두 컨테이너 모두 중복을 허용하지 않는다는 공통점이 있으므로, 프로젝트의 요구 사항에 맞게 적절히 선택하여 사용하시기 바랍니다.