도수 정렬(Counting Sort)은 비교 연산을 사용하지 않고 정렬하는 알고리즘이다. 각 숫자의 등장 횟수를 세어 정렬하는 방식으로 동작하며, 특히 데이터의 크기 범위가 제한적이고 정수 값으로 표현될 때 효율적으로 사용할 수 있다.
도수 정렬은 다음 4단계로 이루어진다:
이제 파이썬 코드와 함께 자세히 살펴보자!
from typing import MutableSequence
def counting_sort(a: MutableSequence) -> None:
n = len(a) # 배열의 크기
f = [0] * (max(a) + 1) # 도수 분포표(Count 배열)
b = [0] * n # 정렬된 데이터를 저장할 보조 배열
# 1단계: 도수 분포표 생성
for i in range(n):
f[a[i]] += 1
# 2단계: 누적 도수 분포표 생성
for i in range(1, max(a) + 1):
f[i] += f[i - 1]
# 3단계: 정렬된 배열 생성 (뒤에서부터 처리)
for i in range(n - 1, -1, -1):
f[a[i]] -= 1
b[f[a[i]]] = a[i]
# 4단계: 정렬된 데이터 원본 배열에 복사
for i in range(n):
a[i] = b[i]
f[a[i]] -= 1을 하는 이유는 0-based index이기 때문에 기본적으로 -1을 처리하며, 다음에 같은 숫자가 중복될 때, 다음 숫자의 위치를 한 칸씩 앞으로 이동하는 역할을 한다.
예제: a = [1, 4, 1, 2, 7, 5, 2]를 도수 정렬로 정렬해보자.
도수 분포표 생성 (각 숫자의 개수를 저장)
f = [0, 2, 2, 0, 1, 1, 0, 1] # 1이 2번, 2가 2번, 4가 1번, 5가 1번, 7이 1번 등장
누적 도수 분포표 생성 (누적합을 구하여 각 숫자의 위치 결정)
f = [0, 2, 4, 4, 5, 6, 6, 7] # 1의 마지막 위치는 2번째 인덱스, 2의 마지막 위치는 4번째 인덱스...
정렬된 배열 생성 (뒤에서부터 채워 넣기)
b = [1, 1, 2, 2, 4, 5, 7] # 정렬된 결과
배열 복사 → 최종적으로 a가 [1, 1, 2, 2, 4, 5, 7]로 정렬됨.
| 경우 | 시간 복잡도 |
|---|---|
| 최선 | O(n + k) |
| 평균 | O(n + k) |
| 최악 | O(n + k) |
n : 정렬할 데이터의 개수k : 데이터의 최댓값k가 크면 공간 낭비가 심해진다.도수 정렬을 활용할 때는 정렬하려는 데이터의 특성을 고려하여 적절한 정렬 알고리즘을 선택하는 것이 중요하다! 😊