基數排序(radix sort)屬於“分配式排序”(distribution sort),又稱“桶子法”(bucket sort)或bin sort,顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些“桶”中,藉以達到排序的作用,基數排序法是屬於穩定性的排序,其時間複雜度爲O (nlog(r)m),其中r爲所採取的基數,而m爲堆數,在某些時候,基數排序法的效率高於其它的穩定性排序法。
實現方法:
基數排序分爲LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由數值的最右邊(低位)開始,而MSD則相反,由數值的最左邊(高位)開始。
1.最高位優先(Most Significant Digit first)法,簡稱MSD法:先按k1排序分組,同一組中記錄,關鍵碼k1相等,再對各組按k2排序分成子組,之後,對後面的關鍵碼繼續這樣的排序分組,直到按最次位關鍵碼kd對各子組排序後。再將各組連接起來,便得到一個有序序列。
2.最低位優先(Least Significant Digit first)法,簡稱LSD法:先從kd開始排序,再對kd-1進行排序,依次重複,直到對k1排序後便得到一個有序序列。
我們把撲克牌的排序看成由花色和麪值兩個數據項組成的主關鍵字排序。
要求如下:
花色順序:梅花 < 方塊 < 紅心 < 黑桃
面值順序:2 < 3 < 4 < … < 10 < J < Q < K < A
那麼,若要將一副撲克牌排成下列次序:
梅花2,…,梅花A,方塊2,…,方塊A,紅心2,…,紅心A,黑桃2,…,黑桃A。
兩種排序方法:
1)先按花色分成四堆,把各堆收集起來;然後對每堆按面值由小到大排列,再按花色從小到大按堆收疊起來。—-稱爲”最高位優先”(MSD)法。
2)先按面值由小到大排列成13堆,然後從小到大收集起來;再按花色不同分成四堆,最後順序收集起來。—-稱爲”最低位優先”(LSD)法。
代碼實現
MSD法實現
#include<iostream>
#include<malloc.h>
using namespace std;
int getdigit(int x,int d)
{
int a[] = {1, 1, 10}; //因爲待排數據最大數據也只是兩位數,所以在此只需要到十位就滿足
return ((x / a[d]) % 10); //確定桶號
}
void PrintArr(int ar[],int n)
{
for(int i = 0; i < n; ++i)
cout<<ar[i]<<" ";
cout<<endl;
}
void msdradix_sort(int arr[],int begin,int end,int d)
{
const int radix = 10;
int count[radix], i, j;
//置空
for(i = 0; i < radix; ++i)
{
count[i] = 0;
}
//分配桶存儲空間
int *bucket = (int *) malloc((end-begin+1) * sizeof(int));
//統計各桶需要裝的元素的個數
for(i = begin;i <= end; ++i)
{
count[getdigit(arr[i], d)]++;
}
//求出桶的邊界索引,count[i]值爲第i個桶的右邊界索引+1
for(i = 1; i < radix; ++i)
{
count[i] = count[i] + count[i-1];
}
//這裏要從右向左掃描,保證排序穩定性
for(i = end;i >= begin; --i)
{
j = getdigit(arr[i], d); //求出關鍵碼的第d位的數字, 例如:576的第3位是5
bucket[count[j]-1] = arr[i]; //放入對應的桶中,count[j]-1是第j個桶的右邊界索引
--count[j]; //第j個桶放下一個元素的位置(右邊界索引+1)
}
//注意:此時count[i]爲第i個桶左邊界
//從各個桶中收集數據
for(i = begin, j = 0;i <= end; ++i, ++j)
{
arr[i] = bucket[j];
}
//釋放存儲空間
free(bucket);
//對各桶中數據進行再排序
for(i = 0;i < radix; i++)
{
int p1 = begin + count[i]; //第i個桶的左邊界
int p2 = begin + count[i+1]-1; //第i個桶的右邊界
if(p1 < p2 && d > 1)
{
msdradix_sort(arr, p1, p2, d-1); //對第i個桶遞歸調用,進行基數排序,數位降 1
}
}
}
void main()
{
int ar[] = {12, 14, 54, 5, 6, 3, 9, 8, 47, 89};
int len = sizeof(ar)/sizeof(int);
cout<<"排序前數據如下:"<<endl;
PrintArr(ar, len);
msdradix_sort(ar, 0, len-1, 2);
cout<<"排序後結果如下:"<<endl;
PrintArr(ar, len);
}
/*
排序前數據如下:
12 14 54 5 6 3 9 8 47 89
排序後結果如下:
3 5 6 8 9 12 14 47 54 89
*/
LSD法實現
#include<iostream>
#include<malloc.h>
using namespace std;
#define MAXSIZE 10000
int getdigit(int x,int d)
{
int a[] = {1, 1, 10, 100}; //最大三位數,所以這裏只要百位就滿足了。
return (x/a[d]) % 10;
}
void PrintArr(int ar[],int n)
{
for(int i = 0;i < n; ++i)
{
cout<<ar[i]<<" ";
}
cout<<endl;
}
void lsdradix_sort(int arr[],int begin,int end,int d)
{
const int radix = 10;
int count[radix], i, j;
int *bucket = (int*)malloc((end-begin+1)*sizeof(int)); //所有桶的空間開闢
//按照分配標準依次進行排序過程
for(int k = 1; k <= d; ++k)
{
//置空
for(i = 0; i < radix; i++)
{
count[i] = 0;
}
//統計各個桶中所盛數據個數
for(i = begin; i <= end; i++)
{
count[getdigit(arr[i], k)]++;
}
//count[i]表示第i個桶的右邊界索引
for(i = 1; i < radix; i++)
{
count[i] = count[i] + count[i-1];
}
//把數據依次裝入桶(注意裝入時候的分配技巧)
for(i = end;i >= begin; --i) //這裏要從右向左掃描,保證排序穩定性
{
j = getdigit(arr[i], k); //求出關鍵碼的第k位的數字, 例如:576的第3位是5
bucket[count[j]-1] = arr[i]; //放入對應的桶中,count[j]-1是第j個桶的右邊界索引
--count[j]; //對應桶的裝入數據索引減一
}
//注意:此時count[i]爲第i個桶左邊界
//從各個桶中收集數據
for(i = begin,j = 0; i <= end; ++i, ++j)
{
arr[i] = bucket[j];
}
}
free(bucket);
}
void main()
{
int br[10] = {20, 80, 90, 589, 998, 965, 852, 123, 456, 789};
cout<<"原數據如下:"<<endl;
PrintArr(br,10);
lsdradix_sort(br, 0, 9, 3);
cout<<"排序後數據如下:"<<endl;
PrintArr(br, 10);
}
/*
原數據如下:
20 80 90 589 998 965 852 123 456 789
排序後數據如下:
20 80 90 123 456 589 789 852 965 998
*/