정렬된 집합 - TreeSet 마스터하기
Java API Reference
import java.util.*;
public class TreeSetBasic {
public static void main(String[] args) {
System.out.println("=== TreeSet 특징 ===\n");
System.out.println("1. 중복 불가");
System.out.println(" - Set 인터페이스\n");
System.out.println("2. 자동 정렬");
System.out.println(" - 오름차순 정렬");
System.out.println(" - Comparable 또는 Comparator\n");
System.out.println("3. 성능");
System.out.println(" - add: O(log n)");
System.out.println(" - contains: O(log n)");
System.out.println(" - remove: O(log n)\n");
System.out.println("4. null 불가");
System.out.println(" - NullPointerException");
}
}public class AutoSort {
public static void main(String[] args) {
// TreeSet (자동 정렬)
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(2);
treeSet.add(8);
treeSet.add(1);
treeSet.add(9);
System.out.println("TreeSet: " + treeSet);
// [1, 2, 5, 8, 9] (정렬됨)
// HashSet (순서 없음)
Set<Integer> hashSet = new HashSet<>();
hashSet.add(5);
hashSet.add(2);
hashSet.add(8);
hashSet.add(1);
hashSet.add(9);
System.out.println("HashSet: " + hashSet);
// 무작위 순서
System.out.println("\n=== 차이점 ===");
System.out.println("TreeSet: 자동 정렬");
System.out.println("HashSet: 순서 없음");
}
}public class TreeSetCreation {
public static void main(String[] args) {
// 기본 생성 (자연 순서)
TreeSet<Integer> set1 = new TreeSet<>();
System.out.println("기본: " + set1);
// 다른 컬렉션으로
TreeSet<Integer> set2 = new TreeSet<>(
Arrays.asList(5, 2, 8, 1, 9)
);
System.out.println("복사: " + set2);
// [1, 2, 5, 8, 9]
// Comparator로 (역순)
TreeSet<Integer> set3 = new TreeSet<>(
Collections.reverseOrder()
);
set3.addAll(Arrays.asList(1, 2, 3, 4, 5));
System.out.println("역순: " + set3);
// [5, 4, 3, 2, 1]
// SortedSet 타입 (권장)
SortedSet<Integer> set4 = new TreeSet<>();
System.out.println("SortedSet 타입: " + set4);
}
}public class CustomSort {
static class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
public static void main(String[] args) {
// 나이순 정렬
TreeSet<Person> set1 = new TreeSet<>(
(p1, p2) -> Integer.compare(p1.age, p2.age)
);
set1.add(new Person("Alice", 25));
set1.add(new Person("Bob", 20));
set1.add(new Person("Charlie", 30));
System.out.println("나이순: " + set1);
// [Bob(20), Alice(25), Charlie(30)]
// 이름순 정렬
TreeSet<Person> set2 = new TreeSet<>(
Comparator.comparing(p -> p.name)
);
set2.add(new Person("Charlie", 30));
set2.add(new Person("Alice", 25));
set2.add(new Person("Bob", 20));
System.out.println("이름순: " + set2);
// [Alice(25), Bob(20), Charlie(30)]
}
}public class TreeSetAdd {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
// 추가 (자동 정렬)
set.add(5);
set.add(2);
set.add(8);
System.out.println("추가 후: " + set);
// [2, 5, 8]
// 중복 추가 (무시)
set.add(5);
System.out.println("중복 후: " + set);
// [2, 5, 8]
// 여러 개 추가
set.addAll(Arrays.asList(1, 9, 3));
System.out.println("여러 개: " + set);
// [1, 2, 3, 5, 8, 9]
// null 추가 (에러!)
try {
set.add(null);
} catch (NullPointerException e) {
System.out.println("\nnull 불가!");
}
}
}public class TreeSetRemove {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>(
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)
);
System.out.println("원본: " + set);
// 요소 삭제
set.remove(5);
System.out.println("5 삭제: " + set);
// 첫 요소 삭제
int first = set.pollFirst();
System.out.println("pollFirst: " + first);
System.out.println("After: " + set);
// 마지막 요소 삭제
int last = set.pollLast();
System.out.println("pollLast: " + last);
System.out.println("After: " + set);
}
}public class TreeSetFirstLast {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>(
Arrays.asList(5, 2, 8, 1, 9, 3)
);
System.out.println("Set: " + set);
// [1, 2, 3, 5, 8, 9]
// 첫 요소
int first = set.first();
System.out.println("First: " + first); // 1
// 마지막 요소
int last = set.last();
System.out.println("Last: " + last); // 9
// 빈 Set
TreeSet<Integer> empty = new TreeSet<>();
try {
empty.first();
} catch (NoSuchElementException e) {
System.out.println("\n빈 Set에서 first() 에러!");
}
}
}public class TreeSetNearest {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>(
Arrays.asList(1, 3, 5, 7, 9)
);
System.out.println("Set: " + set);
// lower: 작은 값 중 가장 큰 것
System.out.println("\nlower(5): " + set.lower(5)); // 3
System.out.println("lower(6): " + set.lower(6)); // 5
// floor: 작거나 같은 값 중 가장 큰 것
System.out.println("\nfloor(5): " + set.floor(5)); // 5
System.out.println("floor(6): " + set.floor(6)); // 5
// ceiling: 크거나 같은 값 중 가장 작은 것
System.out.println("\nceiling(5): " + set.ceiling(5)); // 5
System.out.println("ceiling(6): " + set.ceiling(6)); // 7
// higher: 큰 값 중 가장 작은 것
System.out.println("\nhigher(5): " + set.higher(5)); // 7
System.out.println("higher(6): " + set.higher(6)); // 7
}
}public class TreeSetSubSet {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>(
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
);
System.out.println("전체: " + set);
// subSet (from 포함, to 미포함)
SortedSet<Integer> sub1 = set.subSet(3, 8);
System.out.println("subSet(3, 8): " + sub1);
// [3, 4, 5, 6, 7]
// subSet (포함 여부 지정)
SortedSet<Integer> sub2 = set.subSet(3, true, 8, true);
System.out.println("subSet(3, true, 8, true): " + sub2);
// [3, 4, 5, 6, 7, 8]
// headSet (처음부터 to 미포함)
SortedSet<Integer> head = set.headSet(5);
System.out.println("headSet(5): " + head);
// [1, 2, 3, 4]
// tailSet (from 포함부터 끝까지)
SortedSet<Integer> tail = set.tailSet(7);
System.out.println("tailSet(7): " + tail);
// [7, 8, 9, 10]
}
}public class TreeSetRangeRemove {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>(
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
);
System.out.println("원본: " + set);
// 3~7 삭제
set.subSet(3, true, 7, true).clear();
System.out.println("3~7 삭제: " + set);
// [1, 2, 8, 9, 10]
}
}public class TreeSetAscending {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>(
Arrays.asList(5, 2, 8, 1, 9, 3)
);
// 향상된 for (오름차순)
System.out.println("=== 오름차순 ===");
for (int num : set) {
System.out.print(num + " ");
}
System.out.println();
// 1 2 3 5 8 9
// Iterator (오름차순)
System.out.println("\n=== Iterator ===");
Iterator<Integer> it = set.iterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
System.out.println();
}
}public class TreeSetDescending {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>(
Arrays.asList(5, 2, 8, 1, 9, 3)
);
// descendingSet (내림차순 뷰)
System.out.println("=== 내림차순 Set ===");
NavigableSet<Integer> desc = set.descendingSet();
System.out.println(desc);
// [9, 8, 5, 3, 2, 1]
// descendingIterator
System.out.println("\n=== descendingIterator ===");
Iterator<Integer> it = set.descendingIterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
System.out.println();
// 9 8 5 3 2 1
}
}public class TreeSetStructure {
public static void main(String[] args) {
System.out.println("=== TreeSet 구조 ===\n");
System.out.println("내부: TreeMap 사용");
System.out.println("구조: 레드-블랙 트리");
System.out.println("특징: 자가 균형 이진 탐색 트리\n");
System.out.println("=== 균형 유지 ===");
System.out.println("삽입/삭제 시 자동 균형");
System.out.println("높이: O(log n) 유지\n");
System.out.println("=== 정렬 보장 ===");
System.out.println("중위 순회 = 정렬 순서");
System.out.println("Comparable 또는 Comparator");
}
}public class TreeSetComplexity {
public static void main(String[] args) {
System.out.println("=== 시간 복잡도 ===\n");
System.out.println("add: O(log n)");
System.out.println(" - 트리 탐색 + 삽입\n");
System.out.println("contains: O(log n)");
System.out.println(" - 이진 탐색\n");
System.out.println("remove: O(log n)");
System.out.println(" - 탐색 + 삭제 + 균형\n");
System.out.println("first/last: O(log n)");
System.out.println(" - 트리 순회\n");
System.out.println("HashSet보다 느림");
System.out.println("하지만 정렬 보장!");
}
}public class PerformanceComparison {
public static void main(String[] args) {
int n = 100000;
// HashSet
Set<Integer> hashSet = new HashSet<>();
long start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
hashSet.add(i);
}
long time1 = System.currentTimeMillis() - start;
// TreeSet
Set<Integer> treeSet = new TreeSet<>();
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
treeSet.add(i);
}
long time2 = System.currentTimeMillis() - start;
System.out.println("HashSet: " + time1 + "ms");
System.out.println("TreeSet: " + time2 + "ms");
System.out.println("\nTreeSet이 느림");
System.out.println("하지만 정렬됨!");
}
}public class TopN {
public static void main(String[] args) {
int[] scores = {85, 92, 78, 95, 88, 76, 90, 82};
// 상위 3개
TreeSet<Integer> topScores = new TreeSet<>(
Collections.reverseOrder()
);
for (int score : scores) {
topScores.add(score);
if (topScores.size() > 3) {
topScores.pollLast(); // 가장 작은 것 제거
}
}
System.out.println("상위 3개: " + topScores);
// [95, 92, 90]
}
}public class RangeQuery {
public static void main(String[] args) {
TreeSet<Integer> ages = new TreeSet<>(
Arrays.asList(15, 23, 31, 19, 45, 28, 37, 52)
);
System.out.println("전체: " + ages);
// 20대 (20~29)
SortedSet<Integer> twenties = ages.subSet(20, 30);
System.out.println("20대: " + twenties);
// [23, 28]
// 30세 이상
SortedSet<Integer> over30 = ages.tailSet(30);
System.out.println("30세 이상: " + over30);
// [31, 37, 45, 52]
}
}public class RankingSystem {
private TreeSet<Integer> scores;
public RankingSystem() {
this.scores = new TreeSet<>(Collections.reverseOrder());
}
public void addScore(int score) {
scores.add(score);
}
public int getRank(int score) {
int rank = 1;
for (int s : scores) {
if (s == score) break;
rank++;
}
return rank;
}
public void printTop5() {
System.out.println("=== Top 5 ===");
int count = 0;
for (int score : scores) {
System.out.println((++count) + ". " + score);
if (count >= 5) break;
}
}
public static void main(String[] args) {
RankingSystem ranking = new RankingSystem();
ranking.addScore(95);
ranking.addScore(87);
ranking.addScore(92);
ranking.addScore(78);
ranking.addScore(88);
ranking.addScore(91);
ranking.printTop5();
System.out.println("\n92점의 순위: " + ranking.getRank(92));
}
}// 배열에서 K번째로 큰 수 찾기
public class Problem1 {
public static int findKthLargest(int[] nums, int k) {
// 코드 작성
return 0;
}
public static void main(String[] args) {
int[] nums = {3, 2, 1, 5, 6, 4};
int k = 2;
System.out.println(k + "번째 큰 수: " + findKthLargest(nums, k));
// 5
}
}정답:
정답 보기
public class Problem1 {
public static int findKthLargest(int[] nums, int k) {
TreeSet<Integer> set = new TreeSet<>(
Collections.reverseOrder()
);
for (int num : nums) {
set.add(num);
}
int count = 0;
for (int num : set) {
count++;
if (count == k) {
return num;
}
}
return -1;
}
// 간단 버전
public static int findKthLargest2(int[] nums, int k) {
TreeSet<Integer> set = new TreeSet<>(
Collections.reverseOrder()
);
for (int num : nums) {
set.add(num);
}
Iterator<Integer> it = set.iterator();
for (int i = 1; i < k; i++) {
it.next();
}
return it.next();
}
}// 범위 내 숫자의 합
public class Problem2 {
public static int rangeSum(TreeSet<Integer> set, int from, int to) {
// 코드 작성
return 0;
}
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>(
Arrays.asList(1, 3, 5, 7, 9, 11, 13, 15)
);
int sum = rangeSum(set, 5, 12);
System.out.println("5~12 합: " + sum);
// 5 + 7 + 9 + 11 = 32
}
}정답:
정답 보기
public class Problem2 {
public static int rangeSum(TreeSet<Integer> set, int from, int to) {
SortedSet<Integer> range = set.subSet(from, true, to, true);
int sum = 0;
for (int num : range) {
sum += num;
}
return sum;
}
// Stream 버전
public static int rangeSum2(TreeSet<Integer> set, int from, int to) {
return set.subSet(from, true, to, true)
.stream()
.mapToInt(Integer::intValue)
.sum();
}
}// 스트림에서 최소 K개 유지 (중복 제거)
public class Problem3 {
static class MinKTracker {
private TreeSet<Integer> set;
private int k;
public MinKTracker(int k) {
// 코드 작성
}
public void add(int num) {
// 코드 작성
}
public List<Integer> getMinK() {
// 코드 작성
return null;
}
}
public static void main(String[] args) {
MinKTracker tracker = new MinKTracker(3);
int[] stream = {5, 2, 8, 2, 1, 9, 3, 1, 4};
for (int num : stream) {
tracker.add(num);
}
System.out.println("최소 3개: " + tracker.getMinK());
// [1, 2, 3]
}
}정답:
정답 보기
public class Problem3 {
static class MinKTracker {
private TreeSet<Integer> set;
private int k;
public MinKTracker(int k) {
this.set = new TreeSet<>();
this.k = k;
}
public void add(int num) {
set.add(num);
// K개 초과 시 가장 큰 것 제거
if (set.size() > k) {
set.pollLast();
}
}
public List<Integer> getMinK() {
return new ArrayList<>(set);
}
public void printCurrent() {
System.out.println("Current: " + set);
}
}
}// 중복 불가 + 자동 정렬
TreeSet<Integer> set = new TreeSet<>();
set.add(5);
set.add(2);
set.add(8);
System.out.println(set); // [2, 5, 8]// 추가/삭제
set.add(5); // O(log n)
set.remove(5); // O(log n)
// 첫/마지막
set.first(); // 최소
set.last(); // 최대
set.pollFirst(); // 최소 + 제거
set.pollLast(); // 최대 + 제거
// 근접 값
set.lower(5); // < 5
set.floor(5); // <= 5
set.ceiling(5); // >= 5
set.higher(5); // > 5
// 범위
set.subSet(3, 8); // [3, 8)
set.headSet(5); // [처음, 5)
set.tailSet(7); // [7, 끝]add: O(log n)
contains: O(log n)
remove: O(log n)
first/last: O(log n)
✅ 자동 정렬 필요
✅ 범위 연산
✅ 최소/최대 빠른 접근
✅ 순위 시스템
❌ 빠른 검색만 (HashSet)
❌ 순서 유지만 (LinkedHashSet)