基數排序

基數排序

透過鍵值的部份資訊進行排序
(radixsort)則是屬于“分配式排序”(distributionsort),基數排序法又稱“桶子法”(bucketsort)或binsort,顧名思義,它是透過鍵值的部份資訊,将要排序的元素分配至某些“桶”中,借以達到排序的作用。基數排序法是屬于穩定性的排序,其時間複雜度為O(nlog(r)m),其中r為所采取的基數,而m為堆數,在某些時候,基數排序法的效率高于其它的比較性排序法。基數排序的發明可以追溯到1887年赫爾曼·何樂禮在打孔卡片制表機(Tabulation Machine)上的貢獻。[1]
  • 中文名:基數排序
  • 外文名:Radix sort
  • 别名:
  • 類别:分配式排序
  • 别稱:“桶子法”
  • 方法:最高位優先法和最低位優先
  • 發明者:赫爾曼·何樂禮
  • 領域:計算機算法

基本解法

第一步

以LSD為例,假設原來有一串數值如下所示:

73,22,93,43,55,14,28,65,39,81

首先根據個位數的數值,在走訪數值時将它們分配至編号0到9的桶子。

第二步

接下來将這些桶子中的數值重新串接起來,成為以下的數列:

81,22,73,93,43,14,55,65,28,39

接着再進行一次分配,這次是根據十位數來分配。

第三步

接下來将這些桶子中的數值重新串接起來,成為以下的數列:

14,22,28,39,43,55,65,73,81,93

這時候整個數列已經排序完畢;如果排序的對象有三位數以上,則持續進行以上的動作直至最高位數為止。LSD的基數排序适用于位數小的數列,如果位數多的話,使用MSD的效率會比較好。

MSD的方式與LSD相反,是由高位數為基底開始進行分配,但在分配之後并不馬上合并回一個數組中,而是在每個“桶子”中建立“子桶”,将每個桶子中的數值按照下一數位的值分配到“子桶”中。在進行完最低位數的分配後再合并回單一的數組中。

效率分析

時間效率:設待排序列為n個記錄,d個關鍵碼,關鍵碼的取值範圍為radix,則進行鍊式基數排序的時間複雜度為O(d(n+radix)),其中,一趟分配時間複雜度為O(n),一趟收集時間複雜度為O(radix),共進行d趟分配和收集。空間效率:需要2*radix個指向隊列的輔助空間,以及用于靜态鍊表的n個指針。

實現方法

最高位優先(Most Significant Digit first)法,簡稱MSD法:先按k1排序分組,同一組中記錄,關鍵碼k1相等,再對各組按k2排序分成子組,之後,對後面的關鍵碼繼續這樣的排序分組,直到按最次位關鍵碼kd對各子組排序後。再将各組連接起來,便得到一個有序序列。

最低位優先(Least Significant Digit first)法,簡稱LSD法:先從kd開始排序,再對kd-1進行排序,依次重複,直到對k1排序後便得到一個有序序列。

實現原理

基數排序的發明可以追溯到1887年赫爾曼·何樂禮在打孔卡片制表機(Tabulation Machine)上的貢獻。它是這樣實現的:将所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零。

然後,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以後,數列就變成一個有序序列。

基數排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由鍵值的最右邊開始,而MSD則相反,由鍵值的最左邊開始。

語言實現

C語言

#include

#include

testBS()

{

int a[]={2,343,342,1,123,43,4343,433,687,654,3};

int *a_p=a;

//計算數組長度

int size=sizeof(a)/sizeof(int);

//基數排序

bucketSort3(a_p,size);

//打印排序後結果

int i;

for(i=0;i

printf("%dn",a[i]);

}

int t;

scanf("%d",t);

}

//基數排序

void bucketSort3(int *p,int n)

{

//獲取數組中的最大數

int maxNum=findMaxNum(p,n);

//獲取最大數的位數,次數也是再分配的次數。

int loopTimes=getLoopTimes(maxNum);

int i;

//對每一位進行桶分配

for(i=1;i<=loopTimes;i++){

sort2(p,n,i);

}

}

//獲取數字的位數

int getLoopTimes(int num)

{

int count=1;

int temp=num/10;

while(temp!=0){

count++;

temp=temp/10;

}

return count;

}

//查詢數組中的最大數

int findMaxNum( int *p,int n)

{

int i;

int max=0;

for(i=0;i

if(*(p+i)>max){

max=*(p+i);

}

}

return max;

}

//将數字分配到各自的桶中,然後按照桶的順序輸出排序結果

void sort2(int *p,int n,int loop)

{

//建立一組桶此處的20是預設的根據實際數情況修改

int buckets={};

//求桶的index的除數

//如798個位桶index=(798/1)%10=8

//十位桶index=( 798/10 )%10=9

//百位桶index=( 798/100 )% 10=7

//tempNum為上式中的1、10、100

int tempNum=(int)pow(10,loop-1);

int i,j;

for( i=0;i

int row_index=(*(p+i)/tempNum)%10;

for(j=0;j<20;j++){

if(buckets[row_index][j]==NULL){

buckets[row_index][j]=*(p+i);

break;

}

}

}

//将桶中的數,倒回到原有數組中

int k=0;

for(i=0;i<10;i++){

for(j=0;j<20;j++){

if(buckets[i][j]!=NULL){

*(p+k)=buckets[i][j];

buckets[i][j]=NULL;

k++;

}

}

}

}

Java語言

public class RadixSort{

public static void sort(int[] number,int d){

int k=0;

int n=1;

int m=1;//控制鍵值排序依據在哪一位

int[][]temp=new int[number.length][number.length];

int[]order=new int[number.length];

while(m<=d){

for(int i=0;i

int lsd=((number[i]/n)%10);

temp[lsd][order[lsd]]=number[i];

order[lsd]++;

}

for(int i=0;i

if(order[i]!=0)

for(int j=0;j

number[k]=temp[i][j];

k++;

}

order[i]=0;

}

n*=10;

k=0;

m++;

}

}

public static void main(String[]args){

int[]data=

{73,22,93,43,55,14,28,65,39,81,33,100};

RadixSort.sort(data,10);

for(int i=0;i

System.out.print(data[i]+"");

}

}

pascal

type link=^node;

node=record

data:integer;

next:link;

end;

var i,j,l,m,k,n:integer;

a:array[1..100]of integer;

s:string;

q,head:array[0..9]of link;

p,p1:link;

begin

readln(n);

writeln('Enter data:');

for i:=1 to n do read(a[i]);

for i:=5 downto 1 do

begin

for j:=0to9 do

begin

new(head[j]);

head[j]^.next:=nil;

q[j]:=head[j]

end;

for j:=1to n do

begin

str(a[j],s);

for k:=1 to 5-length(s)do

s:='0'+s;

m:=ord(s[i])-48;

new(p);

p^.data:=a[j];

p^.next:=nil;

q[m]^.next:=p;

q[m]:=p;

end;

l:=0;

for j:=0to9 do

begin

p:=head[j];

while p^.next<>nil do

begin

l:=l+1;p1:=p;p:=p^.next;dispose(p1);a[l]:=p^.data;

end;

end;

end;

writeln('Sorted data:');

for i:=1to n do

write(a[i]:6);

end.

c++

int maxbit(int data[],int n)//輔助函數,求數據的最大位數

{

int d=1;//保存最大的位數

int p=10;

for(int i=0;i

{

while(data[i]>=p)

{

p*=10;

++d;

}

}

return d;

}

{

int d=maxbit(data,n);

int*tmp=new int[n];

int*count=new int;//計數器

int i,j,k;

int radix=1;

for(i=1;i<=d;i++)//進行d次排序

{

for(j=0;j<10;j++)

count[j]=0;//每次分配前清空計數器

for(j=0;j

{

k=(data[j]/radix)%10;//統計每個桶中的記錄數

count[k]++;

}

for(j=1;j<10;j++)

count[j]=count[j-1]+count[j];//将tmp中的位置依次分配給每個桶

for(j=n-1;j>=0;j--)//将所有桶中記錄依次收集到tmp中

{

k=(data[j]/radix)%10;

t

}

for(j=0;j

data[j]=tmp[j];

radix=radix*10;

}

}

C#實現基數排序

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace LearnSort

{

class Program

{

static void Main(string[] args)

{

int[]arr=CreateRandomArray(10);//産生随機數組

Print(arr);//輸出數組

RadixSort(ref arr);//排序

Print(arr);//輸出排序後的結果

Console.ReadKey();

}

public static void RadixSort(ref int[]arr)

{

int iMaxLength=GetMaxLength(arr);

RadixSort(ref arr,iMaxLength);

}

//排序

private static void RadixSort(ref int[] arr,int iMaxLength)

{

List list=new List();//存放每次排序後的元素

List[]listArr=new List;//十個桶

char currnetChar;//存放當前的字符,比如說某個元素123中的2

string currentItem;//存放當前的元素,比如說某個元素123

for(int i=0;i

listArr[i]=new List();

for(int i=0;i

{

foreach(int number in arr)//分桶

{

currentItem=number.ToString();//将當前元素轉化成字符串

try{currnetChar=currentItem[currentItem.Length-i-1];}//從個位向高位開始分桶

catch{listArr.Add(number);continue;}//如果發生異常,則将該數壓入listArr。比如說5 是沒有十位數的,執行上面的操作肯定會發生越界異常的,這正是期望的行為,我們認為5的十位數是0,所以将它壓入listArr的桶裡。

switch(currnetChar)//通過currnetChar的值,确定它壓人哪個桶中。

{

case'0':listArr.Add(number);break;

case'1':listArr.Add(number);break;

case'2':listArr.Add(number);break;

case'3':listArr.Add(number);break;

case'4':listArr.Add(number);break;

case'5':listArr.Add(number);break;

case'6':listArr.Add(number);break;

case'7':listArr.Add(number);break;

case'8':listArr.Add(number);break;

case'9':listArr.Add(number);break;

default:throw new Exception("unknow error");

}

}

for(int j=0;j

foreach(int number in listArr[j].ToArray())

{

list.Add(number);

listArr[j].Clear();//清空每個桶

}

arr=list.ToArray();//arr指向重新排列的元素

//Console.Write("{0}times:",i);

Print(arr);//輸出一次排列的結果

list.Clear();//清空list

}

}

//得到最大元素的位數

private static int GetMaxLength(int[]arr)

{

int iMaxNumber=Int32.MinValue;

foreach(int i in arr)//遍曆得到最大值

{

if(i>iMaxNumber)

iMaxNumber=i;

}

return iMaxNumber.ToString().Length;//這樣獲得最大元素的位數是不是有點投機取巧了...

}

//輸出數組元素

public static void Print(int[]arr)

{

foreach(int i in arr)

System.Console.Write(i.ToString()+'t');

System.Console.WriteLine();

}

//産生随機數組。随機數的範圍是0到1000。參數iLength指産生多少個随機數

public static int[]CreateRandomArray(int iLength)

{

int[] arr=new int[iLength];

Random random=new Random();

for (int i=0;i

arr[i]=random.Next(0,1001);

return arr;

}

}

}

AAuto

第一步

io.open();//打開控制台

/*

*-------------------------------------------------------

*基數排序

**------------------------------------------------------

*/

/*

第二步

基數排序從低位到高位進行,使得最後一次計數排序完成後,數組有序。

其原理在于對于待排序的數據,整體權重未知的情況下,

先按權重小的因子排序,然後按權重大的因子排序。

例如比較時間,先按日排序,再按月排序,最後按年排序,僅需排序三次。

但是如果先排序高位就沒這麼簡單了。

基數排序源于老式穿孔機,排序器每次隻能看到一個列,

很多教科書上的基數排序都是對數值排序,數值的大小是已知的,與老式穿孔機不同。

将數值按位拆分再排序,是無聊并自找麻煩的事。

算法的目的是找到最佳解決問題的方案,而不是把簡單的事搞的更複雜。

基數排序更适合用于對時間、字符串等這些整體權值未知的數據進行排序。

這時候基數排序的思想才能體現出來,例如字符串,如果從高位(第一位)往後排就很麻煩。

而反過來,先對影響力較小,排序排重因子較小的低位(最後一位)進行排序就非常簡單了。

這時候基數排序的思想就能體現出來。

又或者所有的數值都是以字符串形式存儲,就象穿孔機一樣,每次隻能對一列進行排序。

這時候基數排序也适用,例如:對{"193";"229";"233";"215"}進行排序

下面我們使用基數排序對字符串進行排序。

對每個位循環調用計數排序。

*/

第三步

//計數排序算法

radix_sort=function(array,maxlen){

//AAuto在字符串索引越界時,會返回0,這使基數排序的實現更加簡單。

//我們首先找出最大的排序長度,然後對于不足此長度的字符串,尾部都假定以0補齊。

//對于超出此長度的位在比較時忽略

if(!maxlen){

maxlen=0;

for(i=1;#array;1){

maxlen=math.max(maxlen,#array[i])

}

}

//else{

//最大排序長度也可以從參數中傳過來,這樣就不用遍曆所有字符串了

//}

第四步

//從字符串的最後一位開始,到第一位

for(pos=maxlen;1;-1){

//按當前位的字節碼計數排序

var array_sorted={};

var count={};

for(i=0;256){

count[i]=0;

}

var bytecode;

for(i=1;#array;1){

//如果pos大于字符串長度,AAuto會返回0,這使基數排序的實現更容易

bytecode=array[i][pos];

count[bytecode]++;//count[n]包含等于n的個數

}

第五步

//統計位置

for(i=1;256;1){

count[i]+=count[i-1];//count[i]包含小于等于i的個數

}

var n;

for(i=#array;1;-1){

n=array[i][pos]

array_sorted[count[n]]=array[i];

count[n]--;//防止相同的元素n再次出現,将計數減一

}

array=array_sorted;

}

return array

}

io.print("----------------")

io.print("基數排序(線性時間排序)")

io.print("----------------")

array={"AAuto is quicker and better,just try it!";"AAuto Quicker";"193";"229";"233";"215";"Hello

Word";"abc";"abcd";"xd";"adcd";"eddd";"ah";"ai";"aj";"ajkk"};

第六步

//排序

array=radix_sort(array)

第七步

//輸出結果

for(i=1;#array;1){

io.print(array[i])

}

execute("pause")//按任意鍵繼續

io.close();//關閉控制台

相關詞條

相關搜索

其它詞條