선형시간 정렬 알고리즘(기수정렬)

Updated:

강의 사이트
  • http://www.kocw.net/home/search/kemView.do?kemId=1148815

선형시간 정렬 알고리즘(기수정렬)

  • N개의 d자리 정수들이라고 가정한다. (꼭 정수라는 게 아니라 Radix 정렬 알고리즘을 보여 주기 위한 하나의 예)
  • 가장 낮은 자리수부터 정렬한다고 가정한다. (Radix Sort)
  • 가장 높은 자리수부터 정렬하는 것이 Bucket Sort라고 한다.
  • 계수 정렬을 이용한다.

1. Radix Sort (기수 정렬)

  • 일의 자리를 먼저 비교한다. 그에 맞추어서 정렬한다.
  • 이번에는 십의 자리를 비교한다. 그에 맞추어서 정렬한다.
  • 이번에는 백의 자리를 비교한다. 그에 맞추어서 정렬한다.
  • 그런데 이렇게 했는데 어떻게 정렬이 되는가?

  • Radix Sort 정렬은 stable 정렬 알고리즘이기 때문이다.
  • stable은 무엇인가? 비교하는 데이터가 같을 경우 먼저 탐색(입력)된 데이터가 먼저 출력 되기 때문이다.
  • 즉, 일의 자리에서 먼저 정렬되었기 때문에, 십의 자리로 정려할 경우 십의 자리가 같다면 먼저 정렬되어 있던 일의 자리 때문에 작은 수가 큰수보다 앞에 나오게 되어 있다.
  • 예를 들어 처음 일의 자리를 가지고 정렬을 했다고 가정하자.
  • 그렇다면 그림에서 왼쪽 두번째 숫자들을 보면 720, 355, 436으로 되어 있다.
  • 다음 십의 자리로 정렬하면 그림에서 왼쪽부터 세번째 숫자들처럼 720, 329, 436이 되어있다.
  • 이때 백의 자리를 빼고 생각하면 20, 29, 36이다. 당연히 20이 29보다 앞에 와야 한다.
  • 다시 돌아가서 그림의 왼쪽에서 두 번째 숫자들을 보면 720이 329보다 위에 있다. 이미 일의 자리로 stable하게 정렬되어 있었기 때문에 720이 먼저 탐색(입력)된 숫자이므로 329보다 720이 앞에 위치 한다.

1.1 Pseudo Code & 시간복잡도

  • O(D X (N + K))
  • D는 숫자의 자릿수
  • N은 숫자의 개수(즉, 데이터 갯수)
  • K는 데이터가 가질 수의 서로 다른 갯수 (위의 예제에서는 10라고 보면 될듯. 각 자리 수 마다 0부터 9까지 가질 수 있으니까)

1.2 구현

  • 위의 그림 예제는 모두 3자리 수였으나 실제와 같이 데이터가 자리 수 상관 없이 있다고 가정하에 구현
public class RadixSort {
	public static void main(String[] args) {
		int[] array=  { 142, 243, 21, 13, 11,7, 86 };
		int[] sort = radixSort(array);

		System.out.print("최종정렬 : ");
		printArray(sort);
	}
	
	private static int[] radixSort(int[] arr) {
		
		int max = getMax(arr); // arr의 최대값을 구한다.
		
		//최대값의 자리수 만큼 반복 수도 코드에서 D라고 보면 된다.
		for(int i = 0; i < (int)Math.log10(max) + 1; i++) {
			arr = linerSort(arr, (int)(Math.pow(10, i)));
			//각 자릿수 기준으로 정렬(계수정렬)
			System.out.print((int)Math.pow(10, i) + "자리로 정렬 : ");
			printArray(arr);
		}
		
		return arr;
	}
	
	static int getMax(int[] data) {
		int mx = data[0];
		for(int i=1; i<data.length; i++) {
			if(data[i] > mx) {
				mx = data[i];
			}
		}
		return mx;
	}
	
	private static int[] linerSort(int[] arr, int k) {
		//k는 arr 데이터들 중 최대값의 자릿수이다.
		int[] sort = new int[arr.length];
		// 어떤 자리수이든 0~9까지 밖에 없으니 누적 가운트 배열 크기는 10이다.
		int[] count = new int[10];
		
		// arr의 수들에 대해서 K의 자리수를 counting 한다.
		for(int i = 0; i < sort.length; i++) {
			count[(arr[i]/k)%10]++;
			/* K가 10일 경우(즉, 10의 자리를 가지고 비교할 경우)
			 * 예로 123일 때 2를 뽑아 내야한다. 따라서 arr[i]를 10으로 나누고
			 * %10을 하면 2가 나온다.
			 */
		}
		
		// 카운팅한 배열 count를 누적 카운트로 바꾼다.
		for(int i = 1; i < count.length; i++) {
			count[i] = count[i-1] + count[i];
		}
		
		// arr을 순환하며 누적 카운트를 이용하여 각 자리수별 기준으로 sort 배열에 정렬한다.
		for(int i = arr.length-1; i >=0 ; i--) {
			sort[count[(arr[i]/k)%10]-1] = arr[i];
			count[(arr[i]/k)%10]--;
		}
		
		return sort;
	}


	private static void printArray(int[] sort) {
		for(int i = 0; i < sort.length; i++) {
			System.out.print(sort[i] + " ");
		}
		
		System.out.println();
	}
}

//-------------------------------------------//

1자리로 정렬 : 21 11 142 243 13 86 7 
10자리로 정렬 : 7 11 13 21 142 243 86 
100자리로 정렬 : 7 11 13 21 86 142 243 
최종정렬 : 7 11 13 21 86 142 243