기수정렬
기수정렬은 다른 정렬들과는 달리 유일하게 비교연산이 없습니다.
정렬 알고리즘들의 성능 한계인 O(nlog2N)을 유일하게 뛰어 넘을 수 있는 알고리즘입니다.
하지만 단점으로는 적용 범위에 한계가 있습니다.
1 5 4 2 3 6 이런 것은 정렬 가능 하지만 11 23 333 2 4 이런 것들은 정렬 불가합니다.
즉 자릿 수가 다를 경우 정렬 불가능 하다는 겁니다.
하지만 이를 위해 여러 알고리즘을 모색하였습니다.
기본적인 한자리 숫자 기수 정렬의 원리를 알아보겠습니다. 우선 버킷에 정렬 숫자들을 담습니다. 버킷은 양동이입니다.
즉 1 4 2 3 이라는 숫자가 있으면 1은 버킷 1에 4는 버킷 4에 2는 버킷 2에 3은 버킷 3에 들어갑니다.
이것을 버킷 순서대로 꺼냅니다.!! 그럼 1 2 3 4 라고 정렬 돼서 나오겠죠 그림으로 보시죠
이런 식으로 되죠.
기수정렬은 이렇듯이 십진수라면 버킷의 개수는 10개입니다.
기수정렬의 방식중 LSD방식과 MSD방식이 있습니다.
LSD방식은 덜 중요 자릿수 부터 정렬해나갑니다. 첫 자리수 부터 정렬 대상입니다.
MSD방식은 가장 큰 자릿수를 대상으로 정렬해나갑니다.
우선 LSD 방식부터 보시죠.
134 224 232 122라는 정렬 대상이 있습니다. 5진수이기 때문에 버킷은 5개만 있으면 되겠죠. ㅎㅎ
우선 첫 째 자리 숫자 4 4 2 2 가 있습니다. 4 4 가 있는 숫자는 버킷 4에 차례대로 넣으시고 2 2 가 있는 숫자는 버킷 2에 넣습니다.
그럼 버킷 2에는 122 / 232 순서로 들어가 있고 버킷 4에는 224/134 순서로 있겠죠. 버킷2에서 차례때로 꺼냅니다. 그담 버킷4에서 차례대로 꺼냅니다.
그럼 232 122 134 224 순서로 돼있을 겁니다. 그 다음 두 번째 자리수 3 2 3 2가 있죠 마찬가지 방법으로 버킷에 넣습니다. 그림으로 보시죠
1단계
2단계
3단계
이렇게 총 3단계로 버킷에 넣고 빼는 과정으로 정렬이 이루어집니다.
MSD방식은 반대로 큰 자리 숫자로 먼저 버킷에 넣고 빼는 과정으로 이루어집니다. 하지만 장점으로는 중간에 정렬이 끝날 수 있지만 중간에 데이터를 점검해야합니다.
구현의 난이도가 상당합니다. 심지어 빅오의 차이도 없습니다. 성능은 동일합니다.
따라서 LSD방식으로 접근해보도록 하겠습니다.
2자리 숫자와 3자리 숫자를 정렬 할 때는 2자리 숫자의 큰 자릿수는 0으로 계산합니다.
여러분들 혹시 숫자들의 자릿수의 숫자를 추출하는 방법에 대해 아시나요 ?? 아시는 분도 있고 모르시는 분들도 있을겁니다.
32라는 숫자에서 2라는 숫자와 3이라는 숫자를 뽑기위해서는 어떻게 접근할까요 ??
32에서 2라는 숫자는 10으로 나눈 나머지가 되지않나요 ??
3은 32에서 10으로 나누고 그 값을 또 10으로 나눠보면 나머지가 3이지 않나요 ?? 이렇게 접근하면됩니다.
즉
첫 번째 자리 추출 method: 숫자%10
두 번째 자리 추출 method: 숫자/10%10
세 번째 자리 추출 method: 숫자/100%10
방식으로 접근 하면 됩니다.
그다음 버킷은 큐를 이용해야겠죠 선입 선출 구조이기 때문에 먼저 들어온게 차례대로 먼저 나가야합니다. 마치 영화표 줄처럼 말이죠 ㅎㅎ
큐는 연결리스트 기반으로 짠 큐를 사용하겠습니다.
우선 큐의 소스파일 을 보이도록 할게요. 주석 처리를 해놔서 이해하시기 편하실겁니다.
<ListBaseQueue.c> 메인파일
<ListBaseQueue.h> 헤더파일
그리고 기수 정렬 코드를 선보이도록 할게요. 이 코드는 위에서 보인 리스트기반 큐를 선언합니다.
버킷은 큐입니다. 총 10개짜리 버킷 큐를 선언합니다.
우선 첫 for문을 이용하여 총 10개의 버킷을 초기화합니다. 이때 버킷은 큐이기 때문에 큐를 초기화 하면됩니다. QueueInit함수를 이용합니다.
RadixSort 함수의 매개변수로 마지막 인자에서 최대 길이만큼 for문을 돌려줍니다. 그래야 자릿수가 적은 숫자들도 비교가 가능합니다 0으로써
그 다음 정렬할 대상 수만큼 반복합니다. 이때 이제 첫 자리 숫자와 등등을 뽑아줍니다. 위에서 말한 공식있죠 그것을 이용하시면 됩니다.
추출한 자리 값을 radix에 넣고 버킷의 인덱스에 넣은 후 Enqueue함수를 통해 큐를 삽입합니다. 정렬 대상 수만큼 반복했다면 즉 버킷에 삽입이 끝났다면
이제 버킷에서 삭제를 하여 추출합니다. 버킷 수만 큼 반복을 하여 비어있지 않을 때 까지 arr배열에 버킷에 있는 숫자를 삭제하여 집어넣습니다.
그다음 첫 , 두번 째 세번째 자리 숫자를 추출하기 위한 숫자를 10배 증가시킵니다.
이렇게 가장 긴 숫자만큼 반복합니다. 그러면 정렬 완료입니다.
성능은 빅오 O(n)입니다.