계수정렬
16 Dec 2019 | Algorithm계수정렬
계수정렬
- 원소 간 비교하지 않고 원소의 빈도 수를 세어 정렬하는 방법이다
- 모든 원소는 양의 정수여야 한다.
- 시간복잡도는 O(n+k)로 보통 k=O(n)일 때 계수정렬을 사용하고 이 경우 시간복잡도는 O(n)이다.
- 안정성을 가지는 정렬이다
- 정렬을 위해 추가 공간이 필요하다
CountingSort
//pseudocode
CountingSort(A,B,k){
//C[0..k]를 새로운 배열로 한다
for(i=0 to k)
C[i]=0;
//C[i]는 이제 i와 같은 원소의 개수를 나타낸다.
for(j=1 to A.length)
C[A[j]]=C[A[j]] + 1;
//C[i]는 이제 값이 i보다 작거나 같은 원소의 갯수를 나타낸다.
for(i=1 to k)
C[i]=C[i]+C[i-1];
for(j=A.length downto 1){
B[C[A[j]]]=A[j];
C[A[j]]=C[A[j]]-1;
}
}
Java로 계수정렬 구현
-
테스트 코드
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; class CountingSortTest { private Integer[] input; private Integer[] output; @BeforeEach public void setUp(){ input = new Integer[]{2,5,3,0,2,3,0,3}; output = new Integer[]{0,0,2,2,3,3,3,5}; } @Test public void test(){ Integer[] result = CountingSort.sort(input); assertArrayEquals(result,output); } }
-
알고리즘
import java.util.Arrays;
import java.util.Collections;
public class CountingSort {
public static Integer[] sort(Integer[] input) {
int max = Collections.max(Arrays.asList(input));
Integer[] result = new Integer[input.length];
Integer[] countArray = new Integer[max+1];
Arrays.fill(countArray,0);
// countArray[i]는 i와 같은 원소의 개수를 나타낸다.
for(int i=0; i<input.length; i++){
countArray[input[i]] = countArray[input[i]] + 1;
}
//countArray[i]는 이제 i보다 작거나 같은 원소의 개수를 나타낸다.
for(int i=1; i<=max; i++){
countArray[i] = countArray[i] + countArray[i-1];
}
for(int i=input.length-1; i>=0; i--){
result[countArray[input[i]]-1] = input[i];
countArray[input[i]] = countArray[input[i]] - 1;
}
return result;
}
}
계수정렬 vs 퀵정렬
-
퀵정렬의 경우 평균적으로 O(nlogn)의 시간복잡도를 가지나, 일반적인 정렬 상황에서 가장 선호되는 알고리즘이다.
-
입력이 int인 배열이라면 최악의 시간복잡도 O(n+k)인 계수정렬을 우선 고려해볼 수 있겠다는 생각이 들었다. 하지만 스택오버플로우에서 특정조건이 아닌 이상 퀵 정렬이 선호되는 이유에 대해 찾을 수 있었다.
-
-
계수정렬은 countArray를 사용하므로 max값이 크다면 배열의 크기가 커져 배열 생성이 많은 비용이 발생한다.
-
만약 largest, smallest 원소 차이가 크다면 countArray에 의미없는 작업을 수행 할 수있다.
// smallest = 1, largest = 99999이면 2~99998은 의미없는 루프를 돌게된다. for(int i=1; i<=max; i++){ countArray[i] = countArray[i] + countArray[i-1]; }
-
계수정렬은 숫자가 아닌 경우 적용할 수 없다
-
ref. 계수정렬 vs 퀵정렬