🔍
정렬·탐색
버블·삽입·병합·퀵 — 구현과 비교
09강: 정렬 알고리즘
기본 정렬 알고리즘을 직접 구현해 시간복잡도·안정성·인-플레이스 여부를 체감합니다.
정렬버블병합퀵
소요 시간
⏱ 약 1.5시간
난이도
📊 중급
선수 조건
🎯 기초 트랙
결과물
기본 정렬 알고리즘을 직접 구현해 시간복잡도·안정성·인-플레이스 여부를 체감합니다.
이 강의에서 배우는 것
- 1버블·삽입·병합·퀵·계수 정렬의 원리와 시간/공간 복잡도를 설명할 수 있다.
- 2각 정렬의 **안정성(stable)** 과 **제자리 정렬(in-place)** 여부를 판별한다.
- 3주어진 문제 조건(n 크기, 데이터 범위, 안정성 요구)에 맞는 정렬을 선택한다.
개요
💡
**Part 03 – 정렬과 탐색** | 난이도: ⭐⭐☆☆☆
왜 정렬을 직접 구현하는가?
코딩 테스트에서 `sorted()` 하나면 끝나는 일이 많지만, 면접과 일부 문제는 정렬 알고리즘의 **작동 원리**와 **트레이드오프**를 직접 설명하거나 구현할 것을 요구합니다.
- **Bubble/Insertion**: 소규모 데이터, 거의 정렬된 데이터에 강점
- **Merge Sort**: 안정 정렬, 연결 리스트, 외부 정렬
- **Quick Sort**: 평균 최속, 캐시 효율 우수
- **Counting/Radix**: 비교 없이 O(n+k)
직접 구현해 보아야 "언제 어떤 정렬을 선택할까"를 몸으로 이해할 수 있습니다.
핵심 개념
시간 / 공간 복잡도 비교표
| 정렬 알고리즘 | 최선 | 평균 | 최악 | 공간 | 안정 | 제자리 |
|---|---|---|---|---|---|---|
| Bubble Sort | O(n) | O(n²) | O(n²) | O(1) | ✅ | ✅ |
| Insertion Sort | O(n) | O(n²) | O(n²) | O(1) | ✅ | ✅ |
| Merge Sort | O(n log n) | O(n log n) | O(n log n) | O(n) | ✅ | ❌ |
| Quick Sort | O(n log n) | O(n log n) | O(n²) | O(log n) | ❌ | ✅ |
| Counting Sort | O(n+k) | O(n+k) | O(n+k) | O(k) | ✅ | ❌ |
| Radix Sort | O(nk) | O(nk) | O(nk) | O(n+k) | ✅ | ❌ |
💡
k = 값의 범위
안정 정렬 (Stable Sort)
같은 키 값을 가진 원소들의 **상대적 순서**가 정렬 후에도 유지되는지 여부.
text
입력: [(A,2), (B,1), (C,2)]
안정 정렬 후: [(B,1), (A,2), (C,2)] ← A가 C보다 앞 (원래 순서 유지)
불안정 정렬: [(B,1), (C,2), (A,2)] ← 순서 보장 없음Merge Sort 동작 도식
text
[5, 2, 8, 1, 9, 3]
↓ 분할
[5, 2, 8] [1, 9, 3]
↓ ↓
[5,2] [8] [1,9] [3]
↓ ↓
[5][2] [8] [1][9] [3]
↓ 병합 ↓ 병합
[2,5] [8] [1,9] [3]
↓ ↓
[2,5,8] [1,3,9]
↓ 최종 병합
[1,2,3,5,8,9]Quick Sort 분할 예시
text
피벗: 5
[3, 8, 5, 1, 9, 2, 7]
↓
[3, 1, 2] | [5] | [8, 9, 7]
↓ ↓
[1,2,3] [7,8,9]
↓
[1,2,3,5,7,8,9]예제로 보기
| 파일 | 내용 |
|---|---|
| `src/01_bubble_insertion.py` | 버블 정렬 + 삽입 정렬, 단계 수 비교 |
| `src/02_merge_sort.py` | 병합 정렬 재귀/반복, 단계 시각화 |
| `src/03_quick_sort.py` | 랜덤 피벗 퀵 정렬, 최악 케이스 토의 |
| `src/04_counting_sort.py` | 계수 정렬 O(n+k), 기수 정렬 개념 |
자주 하는 실수 5가지
- **퀵 정렬 피벗을 항상 맨 앞/뒤 원소로 고정** - 이미 정렬된 배열에서 O(n²) 최악 케이스 발생 - 해결: 랜덤 피벗(`random.randint`) 또는 median-of-three 사용
- **안정 정렬이 필요한 문제에 불안정 정렬 사용** - 다중 키 정렬(예: 이름 → 점수 순)에서 순서가 틀려짐 - 해결: `sorted()` (Timsort, 안정) 또는 Merge Sort 사용
- **Counting Sort에서 값 범위 k를 배열 길이 n으로 착각** - 값 범위가 매우 클 때 메모리 초과 - 해결: k ≤ 10⁶ 정도일 때만 사용
- **In-place Quick Sort에서 인덱스 범위 착오** - `left <= right` 와 `left < right` 구분 실수 - 단위 테스트: 길이 0, 1, 2 배열 먼저 검증
- **Merge Sort 보조 배열을 매 재귀마다 새로 할당** - O(n log n) 공간이 돼 버림 - 해결: 전역 혹은 상위 레벨에서 한 번 할당
정리
| 상황 | 추천 정렬 |
|---|---|
| 일반적인 코딩 테스트 | `sorted()` (Timsort) |
| 안정 + 연결 리스트 | Merge Sort |
| 평균 속도 최우선 | Quick Sort (랜덤 피벗) |
| 값 범위 작고 n 큼 | Counting Sort |
| 이미 거의 정렬됨 | Insertion Sort |
직접 해 보기
- 길이 10인 배열을 버블 정렬로 정렬하면서 **교환 횟수**를 세어 보세요.
- 같은 배열을 삽입 정렬로 정렬하면서 **이동 횟수**를 세어 보세요.
- 병합 정렬 코드에서 `merge` 함수가 호출되는 횟수를 세어 보세요.
- 퀵 정렬에서 피벗을 항상 첫 번째 원소로 고정하면 `[1,2,3,4,5]` 입력 시 몇 번 비교가 일어나는지 계산해 보세요.
과제
`homework/README.md` 참조