经典的排序算法,计数排序,用到了桶的概念

0准备

 1//建立一个排序用的顺序表结构L
 2#define MAXSIZE 9
 3typedef struct SqList
 4{
 5    int r[MAXSIZE];
 6    int length;
 7}SqList;
 8/*排序的交换函数*/
 9void swap(SqList* L,int i,int j)
10{
11    int temp = L->r[i];
12    L->r[i] = L->r[j];
13    L->r[j] = temp;
14}

计数排序(count Sort)

一个排序时不比较元素大小的排序算法。

计数排序对一定范围内的整数排序时候的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进行排序,而且待排序元素值分布较连续、跨度小的情况。

如果一个数组里所有元素都是整数,而且都在0-K以内。那对于数组里每个元素来说,如果能知道数组里有多少项小于或等于该元素,就能准确地给出该元素在排序后的数组的位置。

C的版本

 1void CountSort(SqList *L) {
 2    int bias, min = L->r[0], max =  L->r[0];
 3    for(int i = 0; i < L->length; i++) {
 4        if (L->r[i] > max) {
 5            max = L->r[i];
 6        }
 7        if (L->r[i] < min) {
 8            min = L->r[i];
 9        }
10    }
11    bias = 0 - min;
12    //初始化计数数组
13    int* countArray= new int[max - min + 1];
14    memset(countArray, 0x0, sizeof(int) * (max - min + 1));
15    //遍历整个原始数组,将原始数组中每个元素值转化为计数数组下标,并将计数数组下标对应的元素值大小进行累加
16    for(int i = 0; i < L->length; i++)
17    {
18        countArray[L->r[i] + bias]++;
19    }
20
21    int index = 0;//访问原始数组时的下标计数器
22    int i = 0;//访问计数数组时的下标计数器
23    //访问计数数组,将计数数组中的元素转化后,重新写回原始数组
24    while(index < L->length)
25    {
26        //只要计数数组中当前下标元素的值不为0,就将计数数组中的元素转换后,重新写回原始数组
27        if(countArray[i] > 0)
28        {
29            L->r[index] = i - bias;
30            countArray[i]--;
31            index++;
32        }else
33        {
34            i++;
35        }
36    }
37    delete[] countArray;
38}

Java代码

 1public int[] CountSort(int[] sourceArray) throws Exception {
 2    // 对 arr 进行拷贝,不改变参数内容
 3    int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
 4
 5    int maxValue = getMaxValue(arr);
 6
 7    return countingSort(arr, maxValue);
 8}
 9
10private int[] countingSort(int[] arr, int maxValue) {
11    int bucketLen = maxValue + 1;
12    //计数数组初始化
13    int[] bucket = new int[bucketLen];
14
15    for (int value : arr) {
16        bucket[value]++;
17    }
18
19    int sortedIndex = 0;
20    for (int j = 0; j < bucketLen; j++) {
21        while (bucket[j] > 0) {
22            arr[sortedIndex++] = j;
23            bucket[j]--;
24        }
25    }
26    return arr;
27}
28
29private int getMaxValue(int[] arr) {
30    int maxValue = arr[0];
31    for (int value : arr) {
32        if (maxValue < value) {
33            maxValue = value;
34        }
35    }
36    return maxValue;
37}

改良版本,用到了最大最小值

 1public int[] CountSort(int[] nums)
 2{
 3    if(0 == nums.length) return nums;
 4    //寻找数组中最大值,最小值
 5    //bias偏移量,用以定位原始数组每个元素在计数数组中的下标位置
 6    int bias, min = nums[0], max = nums[0];
 7    for(int i = 1; i < nums.length; i++)
 8    {
 9        if(nums[i] > max)
10        {
11            max = nums[i];
12        }
13        if(nums[i] < min)
14        {
15            min = nums[i];
16        }
17        bias = 0 - min;
18        //获取计数数组的容量
19        int[] countArray = new int[max - min + 1];
20        Array.fill(counterArray, 0);
21        //遍历整个原始数组,将原始数组中每个元素值转化为计数数组下标,并将计数数组下标对应的元素值大小进行累加
22        for(int i = 0; i < nums.length; i++)
23        {
24            countArray[nums[i] + bias]++;
25        }
26        PrintArray.print(countArray);
27        int index = 0;//访问原始数组时的下标计数器
28        int i = 0;//访问计数数组时的下标计数器
29        //访问计数数组,将计数数组中的元素转化后,重新协会原始数组
30        while(index < nums.length)
31        {
32            //只要计数数组中当前下标元素的值不为0,九江计数数组中的元素转换后,重新写回原始数组
33            if(countArray[i] != 0)
34            {
35                nums[index] = i - bias;
36                countArray[i]--;
37                index++;
38            }else
39            {
40                i++;
41            }
42            PrintArray.print(countArray);
43        }
44    }
45}

计数数组的遍历

最终得到

实际应用

我们会同时找出数组中的maxmin,主要是为了尽量节省空间。试想[1003,1001,1030,1050]这样的数据要排序,真的需要建立长度为1050+1的数组吗?我们只需要长度为1050-1003+1=48的数组(先不考虑额外+1的长度),就能囊括从最小到最大元素之间的所有元素了。

如果待排序数组的元素值跨度很大,比如[99999,1,2],为三个元素排序要使用99999-1+1的空间,实在是浪费。所以计数排序适用于待排序元素值分布较连续、跨度小的情况。