基数排序

来源:互联网 发布:看监控的软件 编辑:程序博客网 时间:2024/04/26 04:19
基数排序

(radix sort)则是属于“分配式排序”(distribution sort),基数排序法又称“桶子法”(bucketsort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O(nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。

解法

  基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significantdigital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。
  以LSD为例,假设原来有一串数值如下所示:
  73,22, 93, 43, 55, 14, 28, 65, 39, 81
  首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
  0
  181
  222
  373 93 43
  414
  555 65
  6
  7
  828
  939
  接下来将这些桶子中的数值重新串接起来,成为以下的数列:
  81,22, 73, 93, 43, 14, 55, 65, 28, 39
  接着再进行一次分配,这次是根据十位数来分配:
  0
  114
  222 28
  339
  443
  555
  665
  773
  881
  993
  接下来将这些桶子中的数值重新串接起来,成为以下的数列:
  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个指针

编辑本段实现的方法

  最高位优先(MostSignificant Digitfirst)法,简称MSD法:先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。
  最低位优先(LeastSignificant Digitfirst)法,简称LSD法:先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。

编辑本段实现原理

  基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机(TabulationMachine)上的贡献。它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
  基数排序的方式可以采用LSD(Leastsignificant digital)或MSD(Most significantdigital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

编辑本段实现

* C

  #include
  #include
  intmain(){
  intdata[10]={73,22,93,43,55,14,28,65,39,81};
  inttemp[10][10]={0};
基数排序算法

  基数排序算法

intorder[10]={0};
  inti,j,k,n,lsd;
  k=0;n=1;
  printf("\n排序前:");
  for(i=0;i<10;i++) printf("%d ",data[i]);
  putchar('\n');
  while(n<=10){
  for(i=0;i<10;i++){
  lsd=((data[i]/n));
  temp[lsd][order[lsd]]=data[i];
  order[lsd]++;
  }
  printf("\n重新排列:");
  for(i=0;i<10;i++){
  if(order[i]!=0)
  for(j=0;j
  data[k]=temp[i][j];
  printf("%d",data[k]);
  k++;
  }
  order[i]=0;
  }
  n*=10;
  k=0;
  }
  putchar('\n');
  printf("\n排序后:");
  for(i=0;i<10;i++) printf("%d ",data[i]);
  return0;
  }

* Java

  publicclass RadixSort {
  publicstatic void sort(int[] number, int d) {
  intk=0;
  intn=1;
  intm=1;//控制键值排序依据在哪一位
  int[][]temp = new int[number.length][number.length];
  int[]order = new int[number.length];
  while(m<= d) {
  for(inti = 0; i < number.length; i++) {
  intlsd = ((number[i] / n) % 10);
  temp[lsd][order[lsd]]= number[i];
  order[lsd]++;
  }
  for(inti = 0; i < d; i++) {
  if(order[i]!= 0)
  for(intj = 0; j < order[i]; j++) {
  number[k]= temp[i][j];
  k++;
  }
  order[i]= 0;
  }
  n*= 10;
  k= 0;
  m++;
  }
  }
  publicstatic void main(String[] args) {
  int[]data =
  {73,22, 93, 43, 55, 14, 28, 65, 39, 81, 33, 100};
  RadixSort.sort(data,10);
  for(inti = 0; i < data.length; i++) {
  System.out.print(data[i]+ " ");
  }
  }



编辑本段c++实现基数排序

  intmaxbit(int data[],int n) //辅助函数,求数据的最大位数
  {
  intd = 1; //保存最大的位数
  intp =10;
  for(inti = 0;i < n; ++i)
  {
  while(data[i]>= p)
  {
  p*= 10;
  ++d;
  }
  }
  returnd;
  } 
  voidradixsort(int data[],int n) //基数排序
  {
  int d = maxbit(data,n);
  int * tmp = new int[n];
  int * count = new int[10]; //计数器
  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 < n; j++)
  {
  k = (data[j]/radix); //统计每个桶中的记录数
  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);
  tmp[count[k]-1] = data[j];
  count[k]--;
  }
  for(j = 0;j < n;j++) //将临时数组的内容复制到data中
  data[j] = tmp[j];
  radix = radix*10;
  }
  delete [] tmp;
  delete [] count;
  }
  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[10];//十个桶
  char currnetChar;//存放当前的字符 比如说 某个元素123 中的2
  string currentItem;//存放当前的元素 比如说 某个元素123
  for (int i = 0; i < listArr.Length;i++)//给十个桶分配内存初始化。
  listArr[i] = new List();
  for (int i = 0; i < iMaxLength;i++)//一共执行iMaxLength次,iMaxLength是元素的最大位数。
  {
  foreach (int number in arr)//分桶
  {
  currentItem = number.ToString();//将当前元素转化成字符串
  try { currnetChar = currentItem[currentItem.Length-i-1];}//从个位向高位开始分桶
  catch { listArr[0].Add(number); continue;}//如果发生异常,则将该数压入listArr[0]。比如说5是没有十位数的,执行上面的操作肯定会发生越界异常的,这正是期望的行为,我们认为5的十位数是0,所以将它压入listArr[0]的桶里。
  switch (currnetChar)//通过currnetChar的值,确定它压人哪个桶中。
  {
  case '0': listArr[0].Add(number); break;
  case '1': listArr[1].Add(number); break;
  case '2': listArr[2].Add(number); break;
  case '3': listArr[3].Add(number); break;
  case '4': listArr[4].Add(number); break;
  case '5': listArr[5].Add(number); break;
  case '6': listArr[6].Add(number); break;
  case '7': listArr[7].Add(number); break;
  case '8': listArr[8].Add(number); break;
  case '9': listArr[9].Add(number); break;
  default: throw new Exception("unknow error");
  }
  }
  for (int j = 0; j < listArr.Length;j++)//将十个桶里的数据重新排列,压入list
  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
  }
  }
  //得到最大元素的位数
  privatestatic int GetMaxLength(int[] arr)
  {
  intiMaxNumber = Int32.MinValue;
  foreach(int i in arr)//遍历得到最大值
  {
  if(i > iMaxNumber)
  iMaxNumber= i;
  }
  returniMaxNumber.ToString().Length;//这样获得最大元素的位数是不是有点投机取巧了...
  }
  //输出数组元素
  publicstatic void Print(int[] arr)
  {
  foreach(int i in arr)
  System.Console.Write(i.ToString()+'\t');
  System.Console.WriteLine();
  }
  //产生随机数组。随机数的范围是0到1000。参数iLength指产生多少个随机数
  publicstatic int[] CreateRandomArray(int iLength)
  {
  int[]arr = new int[iLength];
  Randomrandom = new Random();
  for(int i = 0; i < iLength; i++)
  arr[i]= random.Next(0,1001);
  returnarr;
  }
  }
  }

编辑本段AAuto语言实现基数排序

  io.open();//打开控制台
  
  
  //计数排序算法
  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){
  //按当前位的字节码计数排序
  vararray_sorted ={};
  varcount = {};
  for(i=0;256){
  count[i]= 0;
  }
  varbytecode;
  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的个数
  }
  varn;
  for(i=#array;1;-1){
  n= array[i][pos]
  array_sorted[count[n] ] = array[i];
  count[n]--;//防止相同的元素n再次出现,将计数减一
  }
  array= array_sorted;
  }
  returnarray
  }
  io.print("----------------")
  io.print("基数排序(线性时间排序 )")
  io.print("----------------")
  array={"AAuto is quicker and better,just try it!";"AAutoQuicker";"193";"229";"233";"215";"HelloWord";"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();//关闭控制台