ABOUT

성능과 운영 안정성을 함께 끌어올리는 개발자입니다.

92% Positional Error Reduction
79% p95 Latency Improvement
90%+ Long Tasks Reduction

2022.02 · 한국장학재단

우수 멘티

한국장학재단 사회 리더 대학생 멘토링 IT

2022.10 · 동작구청

우수 인재상

동작구청 우수 SW 인재

2025.05 · (주) 그랩

프로그래밍 우수상

(주) 그랩 우수 프로그램 개발

2025.05 · AWSKRUG

AWS한국사용자모임 발표

AI agent 스크립트 튜닝 관련 발표

ComputerScience

Development

Engineering

Trouble Shooting

GUESTBOOK

첫 마음부터
함께 나누는 온기

방명록 작성하러 가기

SUBSCRIBE

최신소식을
편하게 만나보세요.

정렬

도입

정렬은 데이터를 보기 좋게 만드는 작업이 아니라, 비교 기준을 통일해 탐색과 그리디를 가능하게 만드는 핵심 전처리다

코딩테스트에서 정렬은 단순히 숫자를 오름차순으로 나열하는 기술이 아니라, 문제를 더 쉬운 형태로 바꾸는 전처리 전략으로 매우 자주 등장합니다. 정렬을 한 번 해 두면 가장 작은 값이나 큰 값을 쉽게 다룰 수 있고, 인접한 값끼리만 비교해도 되는 구조가 만들어지며, 투 포인터, 이분 탐색, 그리디, 구간 병합 같은 대표 패턴으로 자연스럽게 이어질 수 있습니다.

특히 자바에서는 Arrays.sort, Collections.sort, List.sort, Comparator를 정확히 이해하고 있으면, 직접 정렬 알고리즘을 구현하지 않아도 대부분의 문제를 안정적으로 해결할 수 있습니다. 실제 코딩테스트에서는 정렬 알고리즘 자체 구현보다, 언제 정렬을 먼저 떠올려야 하는지어떤 기준으로 정렬해야 하는지를 판단하는 능력이 훨씬 중요합니다.

필요성

정렬을 잘 쓰면 브루트포스 문제를 투 포인터, 이분 탐색, 그리디 문제로 바꿔 O(n²)을 O(n log n) 또는 O(n) 수준으로 줄일 수 있다

정렬 전에는 모든 원소를 서로 비교해야 해서 두 겹 반복문이 필요한 문제도 많습니다. 하지만 정렬을 한 뒤에는 인접한 값만 보거나, 조건을 만족하는 경계를 이분 탐색으로 찾거나, 가장 빠른 종료 시점부터 그리디하게 선택할 수 있게 됩니다. 즉, 정렬은 단순한 보조 기술이 아니라 문제의 시간 복잡도를 바꾸는 핵심 도구입니다.

정렬이 특히 강한 문제 유형
  • 최솟값 / 최댓값 기준 선택 문제
  • 회의실 배정, 구간 병합 같은 interval 문제
  • 두 수의 합, 차이, 범위 탐색 문제
  • 좌표 압축, 순위 매기기 문제
  • 문자열 / 객체 다중 조건 정렬 문제
  • 정렬 후 인접 비교가 핵심인 문제
  • 정렬 + 이분 탐색 결합 문제

판단 신호

문제를 읽을 때 “순서가 중요해 보이거나, 가까운 값끼리만 비교해도 될 것 같다면” 정렬을 먼저 의심해야 한다
  • “가장 작은 것부터”, “끝나는 시간이 빠른 순으로” 같은 표현이 보인다.
  • 정렬 후 인접 원소끼리만 비교하면 될 것 같다.
  • 값의 크기 순서를 이용하면 조건 판별이 쉬워질 것 같다.
  • 특정 값 이상 / 이하의 첫 위치를 찾아야 한다.
  • 구간들이 겹치는지, 이어지는지 판단해야 한다.
  • 원래 값이 아니라 순위나 압축된 번호가 필요하다.
실전 팁
코딩테스트에서 정렬은 보통 문제의 본체가 아니라 전처리입니다. 정렬 자체보다, 정렬 후 무엇을 할지가 더 중요합니다.

대표 알고리즘

코딩테스트에서는 모든 정렬을 직접 구현할 필요는 없지만, 각 알고리즘의 성격과 쓰임은 반드시 구분할 수 있어야 한다
알고리즘 시간 복잡도 특징 코테에서의 위치
버블 정렬 O(n²) 구현은 쉽지만 매우 느림 개념 이해용, 실전 사용 거의 없음
선택 정렬 O(n²) 매 단계 최솟값 선택 학습용, 실전 사용 거의 없음
삽입 정렬 O(n²) 거의 정렬된 데이터에 상대적으로 강함 개념 이해용, 소규모 데이터에서만 제한적
병합 정렬 O(n log n) 분할 정복, 큰 데이터에도 안정적 정렬 기반 문제 이해에 중요
퀵 정렬 평균 O(n log n) 실전에서 빠르지만 최악 O(n²) 개념 이해용, 라이브러리 활용이 일반적
힙 정렬 O(n log n) 우선순위 기반 처리와 연결됨 정렬 자체보다 PriorityQueue 문제에서 중요
계수 정렬 O(n + k) 정수 범위가 작을 때 매우 강력 조건만 맞으면 직접 구현 가치가 큼
실전 기준
실제 코딩테스트에서는 버블, 선택, 삽입 정렬을 직접 구현하는 경우는 드뭅니다. 대부분은 라이브러리 정렬 + Comparator로 해결하고, 계수 정렬처럼 제약이 명확할 때만 직접 구현을 고려합니다.

자주 쓰는 정렬 방법

자바 코딩테스트에서는 Arrays.sort, List.sort, Comparator를 정확히 다루는 것이 가장 중요하다
Arrays.sort(arr);                               // 기본 오름차순
list.sort(Comparator.naturalOrder());          // 리스트 오름차순
list.sort(Comparator.reverseOrder());          // 리스트 내림차순
Arrays.sort(words, Comparator.naturalOrder()); // 객체 배열 정렬
Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0])); // 사용자 기준 정렬
실전 팁
primitive 배열객체 배열은 다르게 다뤄야 합니다. int[]는 기본 정렬만 바로 가능하고, Comparator를 쓰려면 Integer[] 같은 객체 배열이 필요합니다.

패턴 1. 기본 오름차순 / 내림차순 정렬

숫자나 문자열을 단순히 정렬하는 것 같아 보여도, 자료형에 따라 코드 방식과 주의점이 달라진다

가장 기본적인 정렬이지만, 자바에서는 int[]Integer[], List<Integer>의 사용 방식이 다릅니다. 특히 내림차순 정렬은 primitive 배열에서 바로 Comparator를 쓸 수 없다는 점을 자주 실수합니다.

int[] nums = {4, 1, 7, 3};
Arrays.sort(nums); // 오름차순

Integer[] arr = {4, 1, 7, 3};
Arrays.sort(arr, Collections.reverseOrder()); // 내림차순

List<Integer> list = new ArrayList<>(Arrays.asList(4, 1, 7, 3));
list.sort(Comparator.reverseOrder());
자주 나오는 문제 형태
  • K번째 수 구하기
  • 최솟값 / 최댓값 빠르게 찾기
  • 숫자 재배치 문제
  • 문자열 사전순 정렬

패턴 2. 다중 조건 정렬

코딩테스트에서 가장 자주 틀리는 정렬은 단순 정렬이 아니라 “첫 번째 기준이 같을 때 두 번째 기준으로 정렬하는” Comparator 문제다

2차원 배열, 객체, 문자열 목록 문제에서는 거의 항상 다중 조건 정렬이 나옵니다. 예를 들어 x좌표 오름차순, 같으면 y좌표 내림차순처럼 기준이 여러 개인 경우가 많습니다.

Arrays.sort(points, (a, b) -> {
    if (a[0] != b[0]) return Integer.compare(a[0], b[0]);
    return Integer.compare(b[1], a[1]);
});
관련 문제 유형
  • 좌표 정렬
  • 나이순 정렬
  • 점수 정렬
  • 길이 우선, 사전순 보조 정렬

패턴 3. 정렬 + 그리디

정렬은 그리디 문제에서 “어떤 순서로 선택해야 최적인지”를 보이게 만들어 주는 전처리다

회의실 배정, 구간 선택, 작업 스케줄링 같은 문제는 정렬을 하지 않으면 그리디 기준이 보이지 않습니다. 대표적으로 종료 시간이 빠른 순으로 정렬한 뒤 가능한 회의를 선택하는 패턴이 자주 나옵니다.

Arrays.sort(meetings, (a, b) -> {
    if (a[1] != b[1]) return Integer.compare(a[1], b[1]);
    return Integer.compare(a[0], b[0]);
});

int count = 0;
int end = 0;

for (int[] meeting : meetings) {
    if (meeting[0] >= end) {
        count++;
        end = meeting[1];
    }
}

이 패턴은 회의실 배정뿐 아니라, 구간 선택, 최소 제거 문제, 자원 배분 문제에도 자주 확장됩니다.

패턴 4. 정렬 + 구간 병합

구간 문제는 시작점을 기준으로 정렬해 두면 겹치는지 여부를 한 번의 순회로 처리할 수 있다

interval 문제의 핵심은 구간들을 정렬한 뒤 현재 구간과 직전 구간만 비교해도 된다는 점입니다. 정렬이 없다면 모든 구간을 다 비교해야 해서 비효율적이지만, 정렬 후에는 선형 순회로 병합이 가능합니다.

Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));

List<int[]> merged = new ArrayList<>();

for (int[] cur : intervals) {
    if (merged.isEmpty() || merged.get(merged.size() - 1)[1] < cur[0]) {
        merged.add(new int[]{cur[0], cur[1]});
    } else {
        merged.get(merged.size() - 1)[1] =
            Math.max(merged.get(merged.size() - 1)[1], cur[1]);
    }
}

패턴 5. 정렬 + 투 포인터

정렬된 배열에서는 왼쪽과 오른쪽 포인터를 움직이며 조건을 만족하는 쌍을 효율적으로 찾을 수 있다

두 수의 합, 차이, 특정 범위 내 쌍 찾기 문제는 정렬 후 투 포인터로 많이 해결합니다. 정렬되지 않은 상태에서는 모든 조합을 확인해야 하지만, 정렬 후에는 합이 작으면 왼쪽을 늘리고 크면 오른쪽을 줄이는 식으로 탐색할 수 있습니다.

Arrays.sort(nums);

int left = 0;
int right = nums.length - 1;

while (left < right) {
    int sum = nums[left] + nums[right];

    if (sum == target) {
        System.out.println("찾음");
        break;
    } else if (sum < target) {
        left++;
    } else {
        right--;
    }
}
관련 문제 유형
  • 두 수의 합
  • 두 수의 차이 최소화
  • 용액 / 수열 쌍 문제
  • 정렬 후 구간 축소 문제

패턴 6. 정렬 + 이분 탐색

정렬된 상태에서는 특정 값의 존재 여부나 경계를 log n에 찾을 수 있기 때문에, 많은 문제에서 정렬 후 이분 탐색이 정석이 된다

특정 값이 존재하는지, target 이상이 처음 나오는 위치가 어디인지, target보다 큰 값 중 최소가 어디인지 같은 문제는 정렬 후 이분 탐색으로 풀립니다. 자바의 Arrays.binarySearch()를 활용할 수도 있고, 직접 lower bound / upper bound를 구현하기도 합니다.

Arrays.sort(arr);

int idx = Arrays.binarySearch(arr, target);

if (idx >= 0) {
    System.out.println("존재함");
} else {
    int insertPos = -idx - 1;
    System.out.println("삽입 위치: " + insertPos);
}

패턴 7. 좌표 압축 / 순위 매기기

값 자체보다 상대적 크기 순서가 중요할 때는 정렬을 통해 원래 값을 압축된 순위로 바꾸는 패턴이 매우 자주 등장한다

좌표 압축은 큰 수나 중복된 값들을 더 작은 인덱스로 치환하는 대표적인 정렬 응용입니다. 정렬 후 중복을 제거하고, 각 값에 순위를 매핑하면 세그먼트 트리, 펜윅 트리, 배열 인덱싱 문제로 쉽게 이어질 수 있습니다.

int[] sorted = Arrays.stream(nums).distinct().sorted().toArray();
Map<Integer, Integer> rank = new HashMap<>();

for (int i = 0; i < sorted.length; i++) {
    rank.put(sorted[i], i);
}

패턴 8. 계수 정렬

정수의 범위가 작고 값의 개수가 많다면 비교 기반 정렬보다 계수 정렬이 훨씬 유리할 수 있다

입력 수는 많지만 값의 범위가 작을 때는 O(n log n) 정렬보다 계수 정렬이 더 빠릅니다. 특히 숫자 범위가 제한된 대량 입력 문제에서 자주 고려합니다. 다만 값의 범위가 매우 크면 메모리 낭비가 심해질 수 있으므로 조건을 먼저 확인해야 합니다.

int max = Arrays.stream(nums).max().getAsInt();
int[] count = new int[max + 1];

for (int num : nums) {
    count[num]++;
}

int idx = 0;
for (int num = 0; num <= max; num++) {
    while (count[num]-- > 0) {
        nums[idx++] = num;
    }
}
계수 정렬이 맞는 상황
  • 숫자 범위가 매우 작다.
  • 입력 크기가 매우 크다.
  • 정렬 기준이 단순한 정수 값이다.
  • 메모리로 count 배열을 감당할 수 있다.

패턴 9. 문자열 정렬

문자열 문제에서는 사전순뿐 아니라 길이 우선, 특정 문자 기준, 접두사 관계까지 고려한 정렬이 자주 나온다

문자열 배열은 단순 사전순 정렬만 나오는 것이 아니라, 길이순 정렬, 길이가 같으면 사전순, 특정 위치 문자 기준 정렬 등 다양한 형태로 출제됩니다. 이때 Comparator를 정확히 설계하는 것이 핵심입니다.

Arrays.sort(words, (a, b) -> {
    if (a.length() != b.length()) return Integer.compare(a.length(), b.length());
    return a.compareTo(b);
});
관련 문제 유형
  • 단어 정렬
  • 파일명 정렬
  • 문자열 길이순 정렬
  • 사용자 정의 문자열 우선순위 정렬

패턴 10. 정렬 기준 설계 자체가 답인 문제

어떤 문제는 정렬 후 로직보다 “정렬 기준을 어떻게 세우느냐”가 사실상 핵심 풀이가 된다

가장 큰 수 만들기, 로그 정렬, 파일명 정렬, 구간 정렬, 문자열 이어붙이기 최적화 같은 문제는 정렬 기준 자체가 문제의 핵심입니다. 이런 문제는 정렬 기준을 제대로 세우지 못하면 이후 로직이 아무리 좋아도 정답이 되지 않습니다.

Arrays.sort(numsAsString, (a, b) -> (b + a).compareTo(a + b));

이런 유형은 “단순 수치 비교”가 아니라, 문제 조건을 비교 함수로 옮기는 능력이 핵심입니다.

자주 하는 실수

코딩테스트에서 정렬 문제를 틀리는 이유는 대개 정렬 기준 누락, 자료형 착각, Comparator 구현 실수 때문이다
  • 동점 처리 기준을 빼먹어서 정답과 다른 순서가 나옴
  • int[]에 바로 내림차순 Comparator를 적용하려고 함
  • a - b 방식 비교를 사용해 overflow가 발생함 → Integer.compare(a, b)가 안전함
  • 정렬 한 번이면 되는데 매 쿼리마다 다시 정렬해서 시간 초과가 남
  • 정렬이 필요한 문제인데 Set, Map, Heap만 보고 끝내 버림
  • 라이브러리 정렬이 충분한데 불필요하게 직접 구현하다가 실수가 발생함
  • 정렬 후 인덱스 의미가 바뀌었는데 원래 인덱스를 따로 관리하지 않음

풀이 루틴

문제를 보면 “정렬 후 무엇이 쉬워지는가”부터 먼저 생각하는 습관이 중요하다
  1. 정렬하면 비교가 쉬워지는지 먼저 본다.
  2. 정렬 기준이 무엇인지 정한다.
  3. 동점일 때 필요한 보조 기준이 있는지 확인한다.
  4. 정렬 후 투 포인터 / 이분 탐색 / 그리디 / 병합 중 무엇으로 이어질지 본다.
  5. 정수 범위가 작다면 계수 정렬이 가능한지도 같이 본다.
  6. primitive 배열인지 객체 배열인지 구분해 적절한 정렬 API를 선택한다.

디버깅

정렬 풀이가 틀릴 때는 정렬 기준, 동점 처리, 정렬 후 로직, 자료형 선택을 먼저 확인해야 한다
1
첫 번째 정렬 기준과 두 번째 정렬 기준이 문제 조건과 정확히 일치하는지 확인한다.
2
정렬 후 인접 비교나 투 포인터 이동 조건이 올바른지 확인한다.
3
Comparator에서 a - b 대신 Integer.compare()를 쓰는지 점검한다.
4
내림차순 정렬이 필요한데 primitive 배열을 그대로 쓰고 있지는 않은지 확인한다.
5
정렬이 필요한 문제가 아니라 Heap, Map, Set이 더 적합한 유형은 아닌지도 함께 확인한다.

요약

코딩테스트에서 정렬은 비교 구조를 단순화하고, 그리디와 투 포인터와 이분 탐색으로 연결해 주는 가장 중요한 전처리 도구 중 하나다
  • ✅ 정렬은 코딩테스트에서 브루트포스를 O(n log n) 이하 풀이로 바꾸는 핵심 전처리다.
  • ✅ 실제 실전에서는 정렬 알고리즘 직접 구현보다 Arrays.sort, List.sort, Comparator 활용이 더 중요하다.
  • ✅ 다중 조건 정렬, 구간 병합, 투 포인터, 이분 탐색, 좌표 압축 문제에서 매우 자주 등장한다.
  • ✅ 정수 범위가 작으면 비교 기반 정렬보다 계수 정렬이 더 적합할 수 있다.
  • ✅ 문제를 보면 “정렬 후 무엇이 쉬워지는가”부터 떠올리는 습관이 중요하다.
728x90