Counting Sort
10 Mar 2016 | 알고리즘 정렬카운팅 정렬(Counting Sort)는 O(N) 성능을 가지는 정렬 알고리즘입니다. 그리고 구현까지 간단해서 알아두면 좋습니다. 흔히 성능을 위해 많이 사용하는 Cache와 비슷한 성격을 갖고 있습니다. O(N) 성능이라고 하면, 그냥 데이터들을 한 번 읽고 지나가면 정렬이 되는 속도라고 볼 수 있습니다.
일반적으로 많이 쓰는 정렬 알고리즘들인 Quick Sort나 Merge Sort가 O(N log N)임을 생각하면 상당히 훌륭한 편입니다. 하지만, 이런 엄청난 속도임에도 정렬 알고리즘의 대표적인 예시가 되지 못하는 이유는 특수한 환경에서만 쓸 수 있는 알고리즘이기 때문입니다.
일단, 원리를 알아보도록 하겠습니다.
원리
Counting Sort의 원리는 다음과 같습니다.
카운팅하고자 하는 숫자 값중 가장 큰 값 만큼의 배열을 하나 만듭니다. 예를 들어서 정렬하고자 하는 값이
{ 5, 14, 2, 7, 11, 3, 6, 10, 9, 18 }
라고 하면, 이 중에서 가장 큰 값이 18이므로 18만큼의 배열을 하나 만듭니다. 넉넉하게 크기 20짜리 배열을 하나 만들어보겠습니다. 저는 아래 코드에서는 result 라는 배열로 만들었습니다. 그리고 데이터를 처음부터 읽으면서 그 값을 인덱스로 하는 칸에 +1 합니다. 예를 들어, 데이터가 숫자 5인 경우는 result[5]를 +1 해주면 됩니다.
이렇게 모든 데이터를 읽어 들인 후, 그 다음에는 result 배열을 처음부터 읽으면서 그 값이 0보다 큰 경우에만 출력을 해주면 됩니다.
코드로 살펴보겠습니다.
코드
const int MAX = 10; int data[MAX] = { 5, 14, 2, 7, 11, 3, 6, 10, 9, 18, }; int result[20]; int main(int argc, char**argv) { for(int i = 0; i < MAX; i++) { int index = data[i]; result[index]++; } for(int i = 0; i <= 20; i++) { if(result[i] > 0) { printf("%d ", i); } } printf("\n"); return 0; }
주의점
성능이 O(N)이긴 하지만, 항상 Quick Sort나 Merger Sort 보다 더 좋은 성능이 나오지는 않습니다. 예를 들어, N = 2, 데이터는 {1, 999999} 라는 데이터가 있으면 카운팅 정렬을 쓸 경우 훨씬 더 안 좋은 성능이 나올 것입니다.
하지만, 일반적인 실 생활에서나 알고리즘 대회 등에서는 카운팅 정렬을 사용할 기회가 많이 있습니다. { 1, 3, 2, 3, 2, 1, 3, 2, 1, } 와 같이 최대값이 적은 경우는 아주 효과가 큽니다. 그리고 꼭 자연수일 필요는 없습니다. 음수일 경우 모든 값에 특정 값을 더해서 양수로 만들어서 적용할 수도 있고, 소수일 경우엔 특정 값을 곱해서 정수처럼 만들어서 사용할 수도 있습니다. 구현도 쉽기 때문에 정렬이 필요할 때 제일 먼저 카운팅 정렬을 떠올려보고 적용이 가능한지 판단해보는 것도 좋을 것 같습니다.