基数排序与计数排序

来源:互联网 发布:java得到项目路径 编辑:程序博客网 时间:2024/05/16 01:53

基数排序

【基本思想】

首先设立r个队列,对列编号分别为0~r-1,r为待排序列中元素的基数(例如10进制数,则r=10),然后按照下面的规则对元素进行分配收集

1,先按最低有效位的值,把n个元素分配到上述的r个队列中,然后从小到大将个队列中的元素依次收集起来
2,再按次低有效位的值把刚收集起来的关键字分配到r个队列中,重复收集工作
3,重复地进行上述分配和收集,直到最高有效位。(也就是说,如果位数为d,则需要重复进行d次,d由所有元素中最长的一个元素的位数计量)

为什么这样就可以完成排序呢?

以从小到大排序为例

首先当按照最低有效位完成分配和收集后,此时得到的序列,是根据元素最低有效位的值从小到大排列的。

当按照次低有效位进行第二次分配和收集后,得到的序列,是先根据元素的次低有效位的值从小到大排列,然后再根据最低有效位的值从小到大排列。

以此类推,当按照最高有效位进行最后一次分配和收集后,得到的序列,是先根据元素的最高有效位的值从小到大排列,再根据次高有效位排列,。。。,再根据次低有效位,再根据最低有效位。自然就完成了每个元素的从小到大排列。

【空间复杂度】O(n+r)

【时间复杂度】

平均情况:O(d(n+r))

因为每一趟分配的时间开销是O(n),收集的开销是O(r),因此执行d趟的时间开销为O(d(n+r)),通常d,r为常数

最好情况:O(d(n+r))

最坏情况:O(d(n+r))

【稳定性】稳定

【优点】

稳定排序;时间复杂度可以突破基于关键字比较排序法的下界O(n)

【缺点】

需要额外的辅助空间

【算法实现】
[java] view plain copy
  1. /** 
  2.  * 基数排序 
  3.  * @param arr 
  4.  */  
  5. public static void radixSort(int[] arr) {  
  6.     //获得待排序列元素中的最大位数  
  7.     int maxBit = getMaxBit(arr);  
  8.     //对每一位分别进行分配和收集  
  9.     for (int bit = 1; bit <= maxBit; bit++) {  
  10.         //分配  
  11.         List<List<Integer>> buf = distribute(arr, bit);  
  12.         //收集  
  13.         collect(arr, buf);  
  14.     }  
  15. }  
  16. private static void collect(int[] arr, List<List<Integer>> buf) {  
  17.     int i = 0;  
  18.     //收集,依次将每个队列中的元素读出  
  19.     for (List<Integer> temp : buf) {  
  20.         for (int ele : temp) {  
  21.             arr[i++] = ele;  
  22.         }  
  23.     }  
  24. }  
  25. private static int getMaxBit(int[] arr) {  
  26.     int maxBit = 0;  
  27.     int len = 0;  
  28.     for (int ele : arr) {  
  29.         //利用字符串的length()方法获得元素位数  
  30.         len = (ele + "").length();  
  31.         if (len > maxBit) {  
  32.             maxBit = len;  
  33.         }  
  34.     }  
  35.     return maxBit;  
  36. }  
  37. //分配  
  38. private static List<List<Integer>> distribute(int[] arr, int bit) {  
  39.     List<List<Integer>> buf = new ArrayList<>();  
  40.     //构建r个队列  
  41.     for (int i = 0; i < 10; i++) {  
  42.         buf.add(new ArrayList<>());  
  43.     }  
  44.     for (int ele : arr) {  
  45.         int value = getValueByBit(ele, bit);  
  46.         //根据每个元素指定位数上的值,将元素存放到对应的队列上  
  47.         buf.get(value).add(ele);  
  48.     }  
  49.     return buf;  
  50. }  
  51. //得到指定位数上的值  
  52. private static int getValueByBit(int ele, int bit) {  
  53.     //没有该位,则返回0  
  54.     int value = 0;  
  55.     String temp = ele + "";  
  56.     if (temp.length() >= bit) {  
  57.         value = (int) (temp.charAt(temp.length() - bit) - '0');  
  58.     }  
  59.     return value;  
  60. }  

【本算法解读】

算法针对的是十进制数,所以r=10

算法首先获取到待排序列元素中的最大位数。然后按照基数排序的思想,对元素的每一位进行分配和收集。

分配distribute()方法:首先构建了r=10个队列,对应编号即是0~9。根据给定的位数,得到待排序列中每个元素在该位上的值,若元素没有该位,则返回0。根据每位上的值,将该元素放入相应的队列。完成一次分配。

收集collect()方法:依次从r个队列中读出其中的元素值,并将其存入原始序列中,即完成了一次收集。

【举个栗子】

对于待排序列413,10,8,28

首先获取到待排序列的最大位数,即为3。

然后根据每一位的值进行分配和收集,分配过程如下图:


由于是10进制数,所以构造10个队列,0~9。

首先从最低有效位开始,每个元素在该位上对应的值分别是3,0,8,8,根据该值,将对应元素放入对应的队列。分配结束后,依次从每个队列上读取元素值存入原始序列。可以看到一次分配以后,元素已经按照最低有效位从小到大排列。继续位数逐渐增大,直到最高有效位,重复上述操作即可完成排序。


计数排序

【基本思想】

计数排序是基于非比较排序,主要用于对于一定范围内的整数进行排序。采用的是空间换时间的方法。

针对待排序列中的每一个元素x,得到序列中小于x的元素个数,有了这一信息可以直接把x放到最终的输出序列的正确位置上。 计数排序之所以能做到线性时间复杂度是因为使用了索引的思想。

计数排序对输入的数据有附加的限制条件:
1、输入的线性表的元素属于有限偏序集S;
2、设输入的线性表的长度为n,|S|=k(表示集合S中元素的总数目为k),则k=O(n)。
在这两个条件下,计数排序的复杂性为O(n)。
对于下面我即将给出的算法,它的限制条件是待排序列中的元素是有限个正整数(包括0,由于将元素值作为数组下标),最大值用k=O(n)表示,元素个数用n表示。它利用元素的实际值来确定它们在输出序列中的位置

【空间复杂度】O(n+k)

【时间复杂度】

平均情况:O(n+k)

最好情况:O(n+k)

最坏情况:O(n+k)

【稳定性】稳定

【优点】

稳定,在k值较小时突破了基于比较的排序算法下界

【缺点】

存在前提条件,需要大量额外空间,k值较大时效率很低

【算法实现】
[java] view plain copy
  1. /** 
  2.  * 计数排序 
  3.  * @param arr 
  4.  */  
  5. public static void countSort(int[] arr) {  
  6.     int max = getMax(arr);  
  7.     int count[] = new int[max + 1];  //此时数组中元素值均被初始化为0  
  8.     //在以元素值为  
  9.     for (int ele : arr) {  
  10.         count[ele]++;  
  11.     }  
  12.     int k = 0;  
  13.     for (int i = 0; i <= max; i++) {  
  14.         for (int j = 0; j < count[i]; j++) {  
  15.             arr[k++] = i;  
  16.         }  
  17.     }  
  18. }  
  19.    //获得待排元素中的最大值  
  20. private static int getMax(int[] arr) {  
  21.     int max = 0;  
  22.     for (int ele : arr) {  
  23.         if (ele > max) {  
  24.             max = ele;  
  25.         }  
  26.     }  
  27.     return max;  
  28. }  

【本算法解读】

可以看到算法首先获得待排序列元素中的最大值,然后构建最大值+1的长度的计数数组。遍历待排序列的每个元素,并在以元素值为下标的数组值中加1。然后遍历计数数组,若对应下标的位置上值大于0(等于几就表示有几个元素),则表示存在有元素且其值为下标的大小。将该元素添加到原始序列中。由于下标是从小到大的,所以对应得到的序列也是从小到大排列的。

【举个栗子】

对于待排序列4,0,2,8,2

首先获取最大值即为0,然后构建8+1长度的计数数组。初始化时,计数数组中的值均为0.如下图所示:


将对应的元素值放在对应的下标里,例如元素4,放在了下标为4的位置里,计数数组的值加1,表示其中有一个元素值为下标的元素。待排序列中有两个2,所以下标为2的计数数组值是2,表示其中有两个元素值为下标的元素。

最后过遍历计数数组,如,遍历到0下标位置,数组值为1,表示有一个元素,元素值是下标0,添加到原始序列中。遍历到2下标位置,数组值为2,表示有两个元素,元素值均为下标2,将两个元素依次添加到原始序列中。以此类推,最终得到有序序列0,2,2,4,8。

0 0
原创粉丝点击