计数排序、桶排序和基数排序

最近面试了一些人,发现你们都忽略了排序算法中的计数排序、桶排序和基数排序,segmentfault中都没有它们的标签就是一明证,呵呵!面试

计数排序

当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。算法

因为用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,须要大量内存。计数排序是用来排序0到100之间的数字的最好的算法,可是它不适合按字母顺序排序人名。可是,计数排序能够用在基数排序中的算法来排序数据范围很大的数组。segmentfault

算法的步骤以下:数组

  1. 找出待排序的数组中最大和最小的元素
  2. 统计数组中每一个值为i的元素出现的次数,存入数组C的第i项
  3. 对全部的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  4. 反向填充目标数组:将每一个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
#define NUM_RANGE (100)    //预约义数据范围上限,即K的值

void counting_sort(int *ini_arr, int *sorted_arr, int n)  //所需空间为 2*n+k
{  
       int *count_arr = (int *)malloc(sizeof(int) * NUM_RANGE);  
       int i, j, k;  

       //初始化统计数组元素为值为零 
       for(k=0; k<NUM_RANGE; k++){  
               count_arr[k] = 0;  
       }  
       //统计数组中,每一个元素出现的次数    
       for(i=0; i<n; i++){  
               count_arr[ini_arr[i]]++;  
       }  

       //统计数组计数,每项存前N项和,这实质为排序过程
       for(k=1; k<NUM_RANGE; k++){  
               count_arr[k] += count_arr[k-1];  
       }  

       //将计数排序结果转化为数组元素的真实排序结果
       for(j=n-1 ; j>=0; j--){  
           int elem = ini_arr[j];          //取待排序元素
           int index = count_arr[elem]-1;  //待排序元素在有序数组中的序号
           sorted_arr[index] = elem;       //将待排序元素存入结果数组中
           count_arr[elem]--;              //修正排序结果,实际上是针对算得元素的修正
       }  
       free(count_arr);  
}

桶排序

个人理解:性能

桶排序是计数排序的变种,把计数排序中相邻的m个"小桶"放到一个"大桶"中,在分完桶后,对每一个桶进行排序(通常用快排),而后合并成最后的结果。code

基本思想:排序

桶排序假设序列由一个随机过程产生,该过程将元素均匀而独立地分布在区间[0,1)上。咱们把区间[0,1)划分红n个相同大小的子区间,称为桶。将n个记录分布到各个桶中去。若是有多于一个记录分到同一个桶中,须要进行桶内排序。最后依次把各个桶中的记录列出来记获得有序序列。内存

效率分析:效率

桶排序的平均时间复杂度为线性的O(N+C),其中C为桶内快排的时间复杂度。若是相对于一样的N,桶数量M越大,其效率越高,最好的时间复杂度达到O(N)。 固然桶排序的空间复杂度 为O(N+M),若是输入数据很是庞大,而桶的数量也很是多,则空间代价无疑是昂贵的。此外,桶排序是稳定的。统计

基数排序

基本思想:

将待排数据中的每组关键字依次进行桶分配。

具体示例:

27八、10九、06三、930、58九、18四、50五、26九、00八、083

咱们将每一个数值的个位,十位,百位分红三个关键字: 278 -> k1(个位)=8,k2(十位)=7,k3=(百位)=2。

而后从最低位个位开始(从最次关键字开始),对全部数据的k1关键字进行桶分配(由于,每一个数字都是 0-9的,所以桶大小为10),再依次输出桶中的数据获得下面的序列。

930、06三、08三、18四、50五、27八、00八、10九、58九、269

再对上面的序列接着进行针对k2的桶分配,输出序列为:

50五、00八、10九、930、06三、26九、27八、08三、18四、589

最后针对k3的桶分配,输出序列为:

00八、06三、08三、10九、18四、26九、27八、50五、58九、930

效率分析:

基数排序的性能比桶排序要略差。每一次关键字的桶分配都须要O(N)的时间复杂度,并且分配以后获得新的关键字序列又须要O(N)的时间复杂度。假如待排数据能够分为d个关键字,则基数排序的时间复杂度将是O(d*2N) ,固然d要远远小于N,所以基本上仍是线性级别的。基数排序的空间复杂度为O(N+M),其中M为桶的数量。通常来讲N>>M,所以额外空间须要大概N个左右。

可是,对比桶排序,基数排序每次须要的桶的数量并很少。并且基数排序几乎不须要任何“比较”操做,而桶排序在桶相对较少的状况下,桶内多个数据必须进行基于比较操做的排序。所以,在实际应用中,基数排序的应用范围更加普遍。

相关文章
相关标签/搜索