内部排序系列 之 分配排序与基数排序

来源:互联网 发布:java数组的定义 编辑:程序博客网 时间:2024/06/06 09:40

     新的学期又开始了.本次的博客将讨论分配排序与选择排序 .

   距离上次的更新已经很久了.本来连贯的思路被期末考给打断了.所以这次的内容可能和这个系列中之前的几次风格上有所差异. 若有错误的地方也请留言指出.不吝赐教.

   还是由易到难.先从分配排序开始.

   <一>分配排序 Binsort

   分配排序是一种非常简单的排序方法. 先看代码:
    
  for(i = 0, i < n , i + +)   B[A[i]] = A[i];

       这是关于分配排序的一个很基本的例子.这里的A[i]既是所需排序的值,也是他排序后的绝对位置.明显的,经过这样的排序处理后可以很轻易的排序完成.但是它的弊端实在是太多了.  首先是它的前提条件, 它必须知道待排序列中的最大值.这样才能分配出足够的空间,有时候这样的要求可谓是苛刻.  其次,所谓的足够的空间在一些情况下是巨大的浪费.
       当然,在某些特定的条件下,这种分配排序是很方便的.毕竟他的复杂度永远都保持在O(n). 
       这里有很多的方法来对这种排序进行优化, 后面介绍的基数排序就是和分配排序非常类似的一种高性能排序方式.

   <二>基数排序 

   基数排序的原理并不复杂. 通过依次不断的对待排序列中的每一个值的某一数位的值单独进行比较后排序,同时还要考虑到之前所进行的其他数位的比较排序所产生的结果,以此保证在这一次的数位基数的排序是正确的.之后不断的对余下数位进行排序即可得出最后的正确结果.明显的,每一次对待排序列的某一单独数位进行的排序,利用前面提到的分配排序来实现是非常简单而且高效的.

   基数排序可以从高位到低位,也可以由低到高.这里简单介绍下由低到高的实现方法.这里要用到10个类似盒子的储存结构,用来存放0-9的结果.第一次的操作之后,所有值的大小将根据各位数值的大小来排列.第二次操作将在10个盒子的基础上依次读入数据进行,这样可以保证十位相同的数据可以依靠第一次排序产生的个位大小顺序进行排列.就这样一直进行下去,经过N次操作之后,可以保证小于N位的待排序列的有序性.

   若是基于链表的实现,则每次只需依次调用next函数来获取下一个在某一位上具有相同数值的数据.直到next=NULL,结束这个盒子的比较.进而开始对下一个盒子内数据的排序.

   如实基于数组的实现,则相对麻烦一点,但是会节省空间,这里就不在多说.
   
   最后是复杂度分析,时间效率:设待排序列为n个记录,d个关键码,关键码的取值范围为radix,则进行链式基数排序的时间复杂度为O(d(n+radix)),其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(n),共进行d趟分配和收集。 空间效率:需要2*radix个指向队列的辅助空间,以及用于静态链表的n个指针。

   在很多情况下也不会采用这样的排序方式,因为它并没有从根本上解决分配排序的尴尬要求.它对数据类型以及数据内容有很大的依赖性,所以导致它的脆弱性.
   下面是实现代码:

void radixsort(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)%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;   mp[count[k]] = data[j];   tcount[k]--;  
        }   
         for(j = 0;j < n;j++) //将临时数组的内容复制到data中   
            data[j] = tmp[j];   radix = radix*10;   
   }   
   delete [] tmp;   delete [] count;   
} 


   下次更新估计会是编译原理学习笔记以及数据库相关内容.本学期将迎来两门非常重要的专业课,加油啦~~希望博客对大家有用.一起学习,一起进步.