桶排序——计数排序和基数排序

桶排序的基本思想:

* 之前所涉及到的简单排序及复杂排序中的归并排序,快速排序和堆排序都属于基于比较的排序
* 而桶排序则是一个不基于比较的排序
*
* 桶排序这类不基于比较的排序的适用范围较为有限
* 其核心思想是把有着相同特点的某些元素装入一个桶,桶与桶之间是有序的,桶的内部也是有序的
* 最终只要按照这种顺序把桶中元素依次倒出即可达到有序
*
* 桶排序适用于已知要排的数组的大致范围,且这个范围不大的情况下,可以使用桶排序来进行

 

1.计数排序:

* 计数排序思想即用到了map数组的思想,去统计范围内每一个值出现的次数,再从小到大去遍历这些可能的值*将其依次输出 

代码实现:

 1 //桶排序之一——计数排序的实现
 2     public static void CountSort(int[] arr,int min,int max) {//min--max为arr的可能取值范围
 3         int len = max - min + 1;
 4         int[] count = new int[len];
 5         for (int i = 0; i < arr.length; i++) {
 6             count[arr[i] - min]++;
 7         }
 8         int cnt = 0;
 9         while (cnt < arr.length) {
10             for (int i = 0; i < len; i++) {
11                 while (count[i] > 0) {
12                     arr[cnt++] = i + min;
13                     count[i]--;
14                 }
15             }
16         }
17     }

 

2.

* 基数排序:(一般针对整形数进行使用(因为其位数已知))
* 其核心思想就是从低位到高位对每一位进行按位排序(因为每一位就10种情况,即范围有限,所以可以使用桶排序思想)
* 只要保证每次低位排完以后,对在同一个桶的元素做到先进先出,那么就可以处理掉每位的排序问题
* 又因为越高位在比较数的大小时优先级越高,越有决定性,所以放到越后面来排

 

代码实现及解析:

 1 //代码实现:(其代码实现中在以上的思想基础上还涉及了诸多优化)
 2     public static void radixSort(int[] arr,int l,int r) {//res为最大数的位数
 3         if (l < r) {
 4             int res = getRes(arr,l,r);
 5             process(arr,l,r,res);
 6         }
 7     }
 8     
 9     //获得数组范围上的最大数的位数
10     public static int getRes(int[] arr,int l,int r) {
11         int res = 0;
12         int max = Integer.MIN_VALUE;
13         for (int i = l; i <= r; i++) {
14             max = Math.max(max, arr[i]);
15         }
16         while (max != 0) {
17             res++;
18             max /= 10;
19         }
20         return res;
21     }
22     
23     //获得x的从右到左的第d位
24     public static int getDigit(int x,int d) {
25         return ((x / ((int)Math.pow(10,d - 1))) % 10);
26     }
27     
28     public static void process(int[] arr,int l,int r,int res) {
29         final int radix = 10;//一般都为10进制数的排序
30         int[] budget = new int[r - l + 1];//用来临时存储每轮处理完后的数组
31         for (int i = 1; i <= res; i++) {//最高位有res位则要进出桶res次,i表示从右到左的第i位
32             int[] count = new int[radix];//用来记录0-9每种数字出现了几次(放到循环内部重新申请,就不用对前一次
33                                          //得到的数组进行置0操作)
34             for (int j = l; j <= r; j++) {//记录当前第i位的0-9的数字的出现次数
35                 int digit = getDigit(arr[j],i);
36                 count[digit]++;
37             }
38             for (int j = 1; j < radix; j++) {//进行前缀和处理,得到的前缀和结果有 "分片" 的作用
39                 count[j] += count[j - 1];
40             }
41             //关键步骤:逆序(可以保证先进先出的思想)通过前缀和的分片作用,调整出这轮排序后的结果,存到budget中
42             for (int j = r; j >= l; j--) {
43                 int digit = getDigit(arr[j],i);
44                 budget[--count[digit]] = arr[j];
45             }
46             //将所得的结果budget存回到原数组arr
47             for (int j = 0; j < budget.length; j++) {
48                 arr[l + j] = budget[j];
49             }
50         }

 

posted @ 2022-04-06 19:59  jue1e0  阅读(53)  评论(0)    收藏  举报