TreeSet

내부적으로 Red-Black Tree로 구현된 정렬된 집합으로, 삽입/삭제와 동시에 정렬 상태를 유지합니다 (java.util.TreeSet).

핵심 특징

PriorityQueue와 차이

PriorityQueueTreeSet
내부 구조이진 힙Red-Black Tree
중복허용불허
최솟값 접근peek() O(1)first() O(log n)
최댓값 접근별도 최대 힙 필요last() O(log n)
임의 원소 삭제O(n)O(log n)

TreeSet은 양 끝을 동시에 효율적으로 다룰 수 있어 이중 우선순위 큐 구현에 적합합니다.

주요 API

메서드설명복잡도
add(e)삽입O(log n)
remove(e)특정 원소 삭제O(log n)
first()최솟값 조회 (삭제 안 함)O(log n)
last()최댓값 조회 (삭제 안 함)O(log n)
pollFirst()최솟값 제거 후 반환O(log n)
pollLast()최댓값 제거 후 반환O(log n)
contains(e)존재 확인O(log n)
size()원소 개수O(1)
isEmpty()비었는지 확인O(1)

이중 우선순위 큐 패턴

TreeSet의 가장 대표적인 활용 패턴. 최댓값·최솟값 삭제를 동시에 지원해야 할 때 사용.

// 내림차순 Comparator → pollFirst()=max, pollLast()=min
TreeSet<Integer> set = new TreeSet<>((a, b) -> b - a);

set.add(3);
set.add(1);
set.add(5);

set.pollFirst();   // 5 (최댓값 삭제)
set.pollLast();    // 1 (최솟값 삭제)
set.first();       // 3 (최댓값 조회, 삭제 안 함)
set.last();        // 3 (최솟값 조회, 삭제 안 함)

기본(오름차순) Comparator를 쓰면 pollFirst()=min, pollLast()=max로 역할이 반대가 됩니다.

프로그래머스 — 이중 우선순위 큐

I x: x 삽입, D 1: 최댓값 삭제, D -1: 최솟값 삭제.

TreeSet<Integer> set = new TreeSet<>((a, b) -> b - a);  // 내림차순

for (String s : operations) {
    String[] str = s.split(" ");
    String command = str[0];
    int num = Integer.parseInt(str[1]);

    if (command.equals("I")) {
        set.add(num);
    } else if (!set.isEmpty()) {
        if (num == 1)  set.pollFirst();   // 최댓값 삭제
        else           set.pollLast();    // 최솟값 삭제
    }
}

return set.isEmpty() ? new int[]{0, 0} : new int[]{set.first(), set.last()};

자주 하는 실수

b - a Comparator 오버플로

// X Integer.MIN_VALUE 근처에서 오버플로
new TreeSet<>((a, b) -> b - a);

// O 안전
new TreeSet<>((a, b) -> Integer.compare(b, a));
new TreeSet<>(Comparator.reverseOrder());

중복 원소 유실

TreeSet<Integer> set = new TreeSet<>();
set.add(3);
set.add(3);   // 무시됨 — size()는 여전히 1

// 중복 허용이 필요하면 TreeMap<Integer, Integer>으로 빈도 관리

TreeSet vs TreeMap

같은 이중 우선순위 큐를 TreeMap으로 구현하면 중복 원소를 처리할 수 있음.

// 중복 허용 이중 우선순위 큐
TreeMap<Integer, Integer> map = new TreeMap<>();  // value = 빈도

// 삽입
map.merge(x, 1, Integer::sum);

// 최댓값 삭제
int max = map.lastKey();
if (map.merge(max, -1, Integer::sum) == 0) map.remove(max);

핵심 정리

→ Red-Black Tree | PriorityQueue

CSLOG에서 보기