Menu
Coddy logo textTech

Counting Sort

마지막 업데이트

계수 정렬은 알려진 제한된 범위의 정수를 위한 비교 없는 정렬입니다. 각 값이 몇 번 나타나는지 세고, 그 계수를 사용해 각 값을 바로 정렬된 위치에 씁니다. 비교가 필요 없습니다. 위의 재생 버튼을 눌러 값이 집계된 뒤 순서대로 배치되는 과정을 확인하세요.

계수 정렬은 O(n + k) 시간에 동작하며, 여기서 k는 입력 값의 범위입니다. kn보다 그리 크지 않을 때 매우 빠르며 O(n log n) 비교 정렬을 능가할 수 있지만, 값의 범위가 거대하면 O(k) 계수 배열 때문에 비현실적이 됩니다.

시간 및 공간 복잡도

경우복잡도비고
시간O(n + k)n개 원소, k = 값의 범위
공간O(n + k)계수 배열 + 출력 배열
안정성접두사 합을 사용해 오른쪽에서 왼쪽으로 배치할 때
비교?아니오비교가 아니라 계수로 정렬함
가장 적합작은 정수 범위k가 n에 가까운 경우

단계별 과정

단계무슨 일이 일어나는가
1계수 배열의 크기를 정하기 위해 최댓값을 찾는다.
2각 값이 몇 번 나타나는지 센다.
3(선택) 안정성을 위해 계수를 접두사 합으로 변환한다.
4각 값을 나타난 횟수만큼 출력에 쓴다.
5출력 배열이 이제 완전히 정렬되었다.

예제 풀이

[1, 4, 1, 2, 4] 정렬하기 (값이 0부터 4까지이므로 계수 배열은 5개의 칸을 가짐):

패스상태동작
입력 순회count = [0, 2, 1, 0, 2]출현 횟수 집계: 1은 두 번, 2는 한 번, 4는 두 번 나타난다.
접두사 합count = [0, 2, 3, 3, 5]각 칸은 이제 자신의 인덱스 <=인 값이 몇 개인지 보관하며, 최종 위치를 제공한다.
4 배치output = [_, _, _, _, 4]오른쪽에서 왼쪽으로 읽음: count[4] = 5이므로 4는 인덱스 4로 간다; 4로 감소시킨다.
2 배치output = [_, _, 2, _, 4]count[2] = 3이므로 2는 인덱스 2로 간다; 2로 감소시킨다.
1 배치output = [_, 1, 2, _, 4]count[1] = 2이므로 1은 인덱스 1로 간다; 1로 감소시킨다.
4 배치output = [_, 1, 2, 4, 4]count[4] = 4이므로 이 4는 인덱스 3으로 간다; 3으로 감소시킨다.
1 배치output = [1, 1, 2, 4, 4]count[1] = 1이므로 이 1은 인덱스 0으로 간다. 배열이 정렬되었다.

계수 정렬을 사용할 때

사용해야 할 때피해야 할 때
정수(또는 정수로 매핑할 수 있는 키)를 작고 알려진 범위에서 정렬할 때.값의 범위 k가 원소 수 n보다 훨씬 클 때.
선형 O(n + k) 시간이 필요하고 추가 배열을 감당할 수 있을 때.메모리가 빠듯할 때 - 계수 배열은 n과 무관하게 O(k) 비용이 든다.
서브루틴으로 안정 정렬이 필요할 때(예: 기수 정렬 내부).키가 부동소수점, 문자열, 또는 정수로 매핑되지 않는 임의의 객체일 때.
최댓값이 유계이고 미리 저렴하게 계산할 수 있을 때.범위가 알려지지 않았거나 무한하여 계수 배열의 크기를 정할 수 없을 때.

Counting Sort 코드

Python, JavaScript, Java, C++, C로 작성된 깔끔하고 실행 가능한 Counting Sort 구현입니다. 언어를 선택해 코드를 복사하거나 Coddy 플레이그라운드에서 바로 열어보세요.

Python로 구현한 Counting Sort 코드

Python
1def counting_sort(a):2    # Works for non-negative integers with a small max value3    counts = [0] * (max(a) + 1)4    for value in a:5        counts[value] += 16    # Prefix sums turn counts into final positions7    for i in range(1, len(counts)):8        counts[i] += counts[i - 1]9    out = [0] * len(a)10    for value in reversed(a):  # reversed keeps equal values stable11        counts[value] -= 112        out[counts[value]] = value13    return out14
15
16nums = [4, 2, 2, 8, 3, 3, 1]17print("Before:", nums)18print("After: ", counting_sort(nums))
이 코드를 Python 플레이그라운드에서 실행하기

계수 정렬 FAQ

계수 정렬의 시간 복잡도는 무엇인가요?
계수 정렬은 O(n + k)이며, 여기서 n은 원소 수, k는 가능한 값의 범위입니다. k = O(n)일 때 이는 선형 시간입니다. 추가로 O(n + k) 공간을 사용합니다.
계수 정렬은 안정적인가요?
그럴 수 있습니다. 안정 버전은 계수의 접두사 합을 만들고 원소를 오른쪽에서 왼쪽으로 배치하여 같은 키의 상대적 순서를 보존합니다. 여기 보인 단순한 "값으로 다시 쓰기" 버전은 올바른 정렬을 만들지만 주로 단순 정수에 사용됩니다.
계수 정렬은 언제 사용해야 하나요?
정수(또는 정수로 매핑할 수 있는 키)를 작고 알려진 범위에서 정렬할 때 사용하세요. 값의 범위 k가 원소 수보다 훨씬 크면 계수 배열이 메모리를 낭비하므로 비교 정렬이 더 낫습니다.
계수 정렬과 기수 정렬(radix sort)의 차이는 무엇인가요?
계수 정렬은 한 번의 패스로 값 전체를 기준으로 정렬하며 값의 범위만큼 큰 계수 배열이 필요합니다. 기수 정렬은 자릿수별로 정렬하고 보통 각 자릿수에 대해 안정적인 계수 정렬을 호출하여 패스당 범위를 작게 유지합니다(예: 10진 자릿수의 경우 10). 기수 정렬은 단일 계수 정렬로는 비현실적이 되는 큰 값의 범위를 처리합니다.
왜 계수 정렬이 항상 퀵정렬(quicksort)보다 빠르지는 않나요?
계수 정렬은 O(n + k)이므로 값의 범위 kn과 비슷할 때만 유리합니다. k가 거대하면 - 예를 들어 0부터 1,000,000,000 범위의 값 100개를 정렬하면 - O(k) 계수 배열이 지배적이 되어 메모리를 낭비하지만, 퀵정렬 같은 O(n log n) 비교 정렬은 빠르고 공간 효율적으로 유지됩니다.
계수 정렬은 음수를 처리할 수 있나요?
네, 작은 오프셋으로 가능합니다. 계수 배열을 값으로 직접 인덱싱하는 대신 value - min으로 인덱싱하여 가장 작은 값이 인덱스 0에 매핑되도록 합니다. 계수 배열의 크기는 max - min + 1이 됩니다. 이 오프셋을 잊는 것은 음수 입력에서 충돌을 일으키는 흔한 버그입니다.
Coddy programming languages illustration

Coddy로 알고리즘을 마스터하세요

시작하기