[Do it! 알고리즘 코딩 테스트 자바] 병합정렬과 기수정렬 - 백준 2751 / 1517 / 10989(9일차)

2026. 3. 6. 21:45·Java

1. 병합 정렬 (Merge Sort)

  • 개념: 배열을 더 이상 나눌 수 없을 때까지 반으로 쪼갠 뒤(Divide), 두 개의 정렬된 부분 배열을 비교하며 하나의 정렬된 배열로 합치는(Merge) 방식을 재귀적으로 반복하는 알고리즘.
  • 핵심 원리 (분기 조건): 투 포인터(i, j)를 사용하여 두 부분 배열의 값을 비교.
    • if (arr[i] <= arr[j]): 왼쪽 값이 작거나 같으면 임시 배열에 넣고 i++ (안정 정렬 유지).
    • else: 오른쪽 값이 작으면 임시 배열에 넣고 j++.
    • 한쪽 배열이 먼저 소진되면, 남은 배열의 원소들을 그대로 뒤에 이어 붙임.
  • 시간 복잡도: 최선, 평균, 최악 모두 O(N \log N) (항상 반으로 나누고 N번 비교하므로 보장됨).
  • 자주 쓰는 상황: 데이터의 상태와 무관하게 항상 일정한 정렬 속도가 필요할 때, 혹은 상대적인 순서가 유지되어야 하는 안정 정렬(Stable Sort)이 필요할 때.

2. 기수 정렬 (Radix Sort)

  • 개념: 데이터를 비교하지 않고, 낮은 자릿수(일의 자리)부터 높은 자릿수까지 차례대로 각 자릿수를 기준으로 정렬을 반복하여 최종적으로 전체를 정렬하는 방식.
  • 핵심 원리 (분기 조건): 값의 '비교' 대신 '빈도수 누적합'을 이용해 자리를 예약.
    • 현재 자릿수를 기준으로 count 배열에 빈도수를 저장하고, 이를 누적합으로 변환하여 각 숫자가 들어갈 '마지막 인덱스'를 계산.
    • 역순 탐색: 원본 배열을 뒤에서부터(i = N-1) 훑으며 자리를 찾아 들어감. 앞선 자릿수 정렬에서 만들어둔 상대적 순서(Stable)를 깨뜨리지 않기 위함.
  • 시간 복잡도: O(kN) (k는 데이터의 최대 자릿수). 비교 연산이 없으므로 O(N \log N)보다 빠를 수 있음.
  • 자주 쓰는 상황: 데이터 개수(N)가 매우 많지만, 데이터의 길이(자릿수)가 짧고 일정할 때. (예: 100만 개의 5자리 이하 숫자 정렬)

 

2751번: 수 정렬하기 2 (병합 정렬 구현)

1. 주요 오개념 및 시행착오 (Pain Point)

  • 반복문(Bottom-Up)으로 병합 정렬을 구현하려다 보니, 데이터 개수가 2의 거듭제곱이 아닐 때 배열 범위를 초과하는 OutOfBounds 에러 제어에 실패함.
  • 정렬 로직 내부에서 두 그룹을 비교하는 조건과, 한쪽 그룹이 소진된 후 남은 값을 처리하는 로직의 경곗값(i <= m 등) 설정이 가장 헷갈렸음. 이를 하나의 복잡한 while문 안에서 break와 if-else로만 욱여넣으려다 논리가 완전히 꼬임.
  • 출력 시 100만 개의 데이터를 System.out.println으로 처리하여 시간 초과가 발생할 위험을 간과함.

2. 핵심 원리 및 수학적 근거 (Logic & Math)

  • 분할과 병합의 독립성: 병합 정렬의 핵심은 merge_sort 재귀 함수로 배열을 더 이상 쪼갤 수 없을 때까지 두 갈래로 계속 분할한 뒤, 쪼개져서 이미 정렬된 상태의 두 배열을 merge 함수로 다시 합치는 것임.
  • 정확한 중앙값 분할: mid = start + (end - start) / 2 공식을 사용하면 배열을 안전하게 반으로 나눌 수 있음.
  • 명확한 경계 조건: 두 그룹을 병합할 때 while (i <= mid && j <= end)처럼 고정된 경곗값(mid, end)을 명확히 설정해야 반복문이 엇나가지 않음.

1번 2번으로 분할되고 1-2로 1번이 분할-병합되면 2번도 3-4으로 분할 병합 후 마지막으로 병합!

3. 설계 통찰

  • 인덱스를 수동으로 쫓아가는 반복문 방식보다, 재귀 방식이 '분할 -> 병합'라는 병합 정렬의 본질적인 로직을 훨씬 직관적이고 안전하게 구현할 수 있음.
  • 병합(merge) 과정은 ① 두 그룹 비교 후 삽입, ② 왼쪽 남은 그룹 털어넣기, ③ 오른쪽 남은 그룹 털어넣기라는 3개의 독립된 while문으로 분리해야 코드가 꼬이지 않음.

4. 구현상의 유의점 (Implementation Wisdom)

  • 병합 결과를 담을 tmp 임시 배열은 재귀 호출마다 새로 생성하지 않고, 전역(static)으로 한 번만 할당하여 메모리 오버헤드를 막아야 함.
  • 대량의 출력은 반드시 StringBuilder나 BufferedWriter를 사용해 한 번에 방출(flush)해야 함.

 

1517번: 버블 소트 (병합 정렬 응용)

1. 주요 오개념 및 시행착오 (Pain Point)

  • 오른쪽 값이 왼쪽으로 넘어올 때 발생하는 교환 횟수를 j - i로 계산함. 다음에 코드를 다시 봤을 때 j와 i가 정확히 어떤 상태를 의미하는지 직관적으로 파악하기 어려웠음.
  • 교환 횟수 count 변수를 int형으로 선언하여, 최대 500,000개의 역순 정렬 시 발생하는 1,250억 번의 연산을 담지 못해 오버플로우로 틀림.

2. 핵심 원리 및 수학적 근거 (Logic & Math)

  • 교환 횟수 = 역전 쌍(Inversion Count): 오른쪽 그룹의 값(arr[j])이 선택된다는 것은, 해당 값이 현재 왼쪽 그룹에 남은 모든 원소들보다 작아서 앞으로 점프한다는 뜻임.
  • j와 m의 본질적 차이: j는 탐색을 진행하며 배열의 끝(end)을 향해 계속 값이 변하는 이동 포인터임.
    • m은 왼쪽 그룹의 끝을 나타내는 고정된 경곗값임.
    • 따라서 점프하는 거리는 변동하는 j가 아니라, 고정된 m을 기준으로 한 m - i + 1(왼쪽 그룹에 남은 실제 원소 개수)로 계산해야 항상 정확함.

3. 설계 통찰

  • 문제 이름이 "버블 소트"라고 해서 O(N^2)인 버블 정렬을 그대로 쓰면 안 됨. 교환이 일어나는 횟수를 병합 정렬의 '역전 쌍' 원리로 치환하여 O(N \log N)으로 해결하는 아이디어가 핵심임.

4. 구현상의 유의점 (Implementation Wisdom)

  • 최악의 시나리오(역순 정렬)를 고려해 결과값이 21억을 초과할 수 있다면 무조건 long count를 사용해야 함.
  • 교환 횟수 누적은 오른쪽 원소가 더 작아서 선택되는 단 한 번의 조건(else 분기)에서만 count += (m - i + 1)로 깔끔하게 처리함.

10989번: 수 정렬하기 3 (기수 정렬 직접 구현)

1. 주요 오개념 및 시행착오 (Pain Point)

  • 처음엔 실제 Queue 10개를 만들거나, 인덱스와 값을 저장하는 Node 클래스를 만들려 했으나 데이터의 차원과 구조가 너무 복잡해져서 포기함.
  • 자릿수를 추출할 때, 10의 자리일 때는 if문으로 나누고, 100의 자리일 때는 또 다른 if문을 쓰는 식의 비효율적인 하드코딩을 시도함.
  • 임시 배열 output의 크기를 전체 개수 N이 아닌 10으로 착각했고, 누적합 배열인 count를 매 자릿수마다 새로 초기화해야 한다는 사실을 놓침.

2. 핵심 원리 및 수학적 근거 (Logic & Math)

  • 수식 하나로 자릿수 추출: (arr[i] / exp) % 10 공식을 사용하면 복잡한 if문 분기 없이 exp가 1, 10, 100으로 커짐에 따라 해당 자릿수의 숫자만 정확히 떼어낼 수 있음.
  • 빈도수와 누적합의 마법: 복잡한 객체나 큐 없이도, 각 숫자의 빈도수(Count)를 구하고 이를 누적합으로 변환하면 "이 그룹은 배열의 몇 번 인덱스까지 차지한다"는 방 예약 시스템이 완성됨.

3. 설계 통찰

  • 큐(Queue) 없이 배열만으로 구현하는 계수 정렬(Counting Sort) 원리를 차용하는 것이 기수 정렬의 최적화 핵심임.
  • 역순 탐색 (Stable Sort): 원본 배열을 반드시 뒤에서부터(i = N-1 -> 0) 훑으며 자리를 배치해야 함. 그래야 앞선 자릿수 정렬에서 기껏 맞춰둔 상대적인 위치(순서)가 파괴되지 않음.

4. 구현상의 유의점 (Implementation Wisdom)

  • 매 자릿수(exp) 루프가 돌 때마다 count = new int[10]을 새로 생성하여 빈도수를 초기화해야 함.
  • 자리를 찾아 들어갈 때 output[count[value] - 1]에서 -1을 하는 이유는, 누적합은 1부터 시작하는 개수이고 배열 인덱스는 0부터 시작하기 때문임.
  • 정렬된 output 배열을 원본 arr로 덮어씌울 때는 for문 대신 네이티브 복사인 System.arraycopy()를 사용하여 속도를 극대화함.
저작자표시 (새창열림)

'Java' 카테고리의 다른 글

[Do it! 알고리즘 코딩 테스트 자바] BFS 너비 우선 탐색 - 백준 1260(11일차)  (0) 2026.03.08
[Do it! 알고리즘 코딩 테스트 자바] 백트래킹을 써야하는 이유(feat.DFS) - 백준 11724/2023/13023/15649/9663(10일차)  (0) 2026.03.08
[Do it! 알고리즘 코딩 테스트 자바] 삽입 정렬과 퀵 정렬 - 백준 11399 / 11004(8일차)  (0) 2026.03.02
[Do it! 알고리즘 코딩 테스트 자바] 버블 정렬과 선택 정렬 - 백준 1377 / 1427(7일차)  (0) 2026.03.01
[Do it! 알고리즘 코딩 테스트 자바] 백준 17298번 오큰수 / 11286번 절댓값 힙(6일차)  (1) 2026.03.01
'Java' 카테고리의 다른 글
  • [Do it! 알고리즘 코딩 테스트 자바] BFS 너비 우선 탐색 - 백준 1260(11일차)
  • [Do it! 알고리즘 코딩 테스트 자바] 백트래킹을 써야하는 이유(feat.DFS) - 백준 11724/2023/13023/15649/9663(10일차)
  • [Do it! 알고리즘 코딩 테스트 자바] 삽입 정렬과 퀵 정렬 - 백준 11399 / 11004(8일차)
  • [Do it! 알고리즘 코딩 테스트 자바] 버블 정렬과 선택 정렬 - 백준 1377 / 1427(7일차)
미우딩
미우딩
다양한 프로그래밍 언어를 공부하고 있는 공대생입니다! Python, Java, C언어를 주로 공부하고 있으며 Figma를 통한 디자인도 종종 올릴 예정입니다.
  • 미우딩
    미우의 코딩 기록
    미우딩
  • 전체
    오늘
    어제
    • 분류 전체보기 (60)
      • Java (47)
      • C언어 (0)
      • 대외활동 (5)
      • Spring (4)
      • 프로젝트 (2)
      • 앱개발 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    IT #백준 #알고리즘 #코딩테스트 #배열 #형변환 #아스키코드
    IT #백준 #코딩테스트 #알고리즘 #구간합 #반복문
    IT #코딩테스트 #알고리즘 #버블정렬 #오름차순
    java #android #개발자
    IT #백준 #알고리즘 #코딩테스트 #배열 #형변환 #평균구하기
    IT #알고리즘 #그리디알고리즘 #탐욕알고리즘 #코테
    IT #코딩테스트 #알고리즘 #슬라이딩윈도우 #투포인터 #함수
    IT #알고리즘 #코딩테스트 #선택정렬 #내림차순정렬 #최댓값
    IT #코딩테스트 #알고리즘 #스택 #
    IT #알고리즘 #코딩테스트 #삽입정렬 #오름차순정렬
    android #java #kotlin #상태관리
    IT #코딩테스트 #알고리즘 #백준 #투포인터 #배열
    IT #큐 #알고리즘 #코딩테스트 #자바 #선입선출
    IT #BFS #코딩테스트 #구현 #큐 #재귀함수
    IT #알고리즘 #코딩테스트 #우선순위큐 #힙 #절댓값 #오름차순 #정렬
    IT #코딩테스트 #이진탐색 #알고리즘
    BufferedReader
    IT #코딩테스트 #알고리즘 #투포인터 #시간제한 #Scanner #BufferedReader #StringTokenizer
    android #java #kotlin #compose
    push #pop #StringBuffer
    Spring #정적컨텐츠 #MVC #템플릿엔진 #API #애너테이션
    IT #알고리즘 #코딩테스트 #DFS
    IT #알고리즘 #코딩테스트 #퀵정렬 #오름차순정렬
    IT #코딩테스트 #알고리즘 #DFS #재귀함수 #무방향그래프
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
미우딩
[Do it! 알고리즘 코딩 테스트 자바] 병합정렬과 기수정렬 - 백준 2751 / 1517 / 10989(9일차)
상단으로

티스토리툴바