두 원소의 키 비교에 의한 정렬 방법이 아닌 정렬 알고리즘으로 기수정렬과 계수정렬이 있다.
정렬할 자료들 : x0, x1, x2, x3, ... x(n-1)
정렬 자료의 최대 자리 개수 : d
진수 : r
(각 자료의 가장 왼쪽 자릿수가 1번째 자릿수이고, 가장 오른쪽 자릿수가 d번째 자릿수이다.)
LSD(Least Significant Digit) Radix Sort
for i in range(d, 0, -1):
bin[0], bin[1], bin[2], ... bin[r-1]을 초기화
for j in range(0, n, 1):
xj의 i번째 자릿수의 digit을 검사하여 xj를 bin[digit]에 넣는다.
bin[0]부터 bin[r-1]까지 원소들을 차례로 모은다.
시간복잡도 : O(d(n+r))
(자릿수가 고정되어있으므로 안정적인 정렬방식이다.)
Counting Sort(계수정렬)은 숫자들간 비교를 하지 않고 정렬을 하는 알고리즘이다.
일일이 비교를 하지 않고 각 숫자가 몇 개인지 센 다음에 정렬을 하기 떄문에 시간복잡도는 O(n)이 나오게 된다.
다만, Counting sort는 모든 리스트에 적용을 할 수는 없고, 일정한 조건을 만족하는 리스트들에만 해당 알고리즘을 적용할 수 있다.
조건들은 다음과 같다:
O(n)이지만, 퀵정렬이나 병합정렬을 더 많이 사용하는 이유는 계수정렬은 각 숫자가 몇 번 나왔는지 세는 배열을 또 하나 만들지만 연속된 숫자가 아닌 1, 2, 3 그리고 갑자기 100이 나온다면 그 빈 공간에 해당하는 공간(메모리) 낭비가 생기기 때문이다.
Algorithm CountingSort(A, B, k)
for i = 0 to k
C[i] = 0;
for j = 0 to n-1 // A원소를 C의 해당 인덱스에 쌓기
C[A[j]] += 1
for i = 0 to k-1
C[i] = C[i] + C[i-1] // 누적해서 원소 값 쌓아 놓기 (B에서 인덱스에 활용하기 위함)
for j = n-1 down to 0
B[C[A[j]] -1] = A[j];
C[A[j]] -= 1;
시간복잡도를 다 더하면 O(n+k)가 된다.
만약 k=O(n) 이면 counting sort의 시간복잡도는 O(n)이 된다.
정렬을 하는 것이 이렇게 빠를 수 있는 이유는 counting sort는 비교를 하지 않기 때문이다.
하지만, k의 값이 너무 커지면 일반 정렬 알고리즘보다 느려질 수 있는 단점이 있다. (그래서 병합 정렬 또는 힙 정렬을 많이 사용함)
Counting sort는 기존 element들의 순서를 지키면서 정렬을 시키기 때문에 stable sort로 분류된다. 하지만, 추가적인 메모리를 요구하기 때문에 in-place 알고리즘은 아니다.
참고 : 어느 행인의 개발 블로그
월클이네요