计数排序及其优化

一、什么是计数排序

        计数排序是利用下标进行排序的算法,不用对要排序的数字进行比较,我们使用下面的例子进行说明。
        假设有20个随机整数,其取值范围在0~10之间,对其进行排序,由于这20个随机整数的取值范围是固定的,那么我们可以定义一个长度为11的数组,数组下标为从0到10,并且元素初始值全为0,然后遍历要排序的20个随机整数,将遍历到的数值对应的数组下标进行+1,直到遍历结束。如下图所示:
        假设我们要遍历的20个随机数为:9,3,5,4,8,9,1,2,7,8,5,3,6,7,9,0,4,7,2,4。创建一个长度为11的数组如下所示:
计数排序及其优化_第1张图片
        然后我们开始遍历,遍历的第一个数字是9,则给新建立的数组下标为9的值+1,即:
计数排序及其优化_第2张图片
        接着我们依此类推,直到遍历完要排序的数,遍历完之后的结果是:
计数排序及其优化_第3张图片
        根据上面的数组的结果,我们就可以得到一个有序的数列,即:0,1,2,2,3,3,4,4,4,5,5,6,7,7,7,8,8,9,9,9。

二、计数排序的实现

public static int[] countSort(int[] array){
        //得到数列的最大值
        int max=array[0];
        for (int i=0;i<array.length;i++){
            if(array[i]>max){
                max=array[i];
            }
        }
        //根据数列最大值确定统计数组的长度
        int[] countArray=new int[max+1];
        //遍历数组,填充统计数组
        for (int i=0;i<array.length;i++){
            countArray[array[i]]++;
        }

        //遍历统计数组,得到排好序后的数组
        int index=0;
        int[] sortedArray=new int[array.length];
        for (int i=0;i<countArray.length;i++){
            for (int j=0;j<countArray[i];j++){
                sortedArray[index++]=i;
            }
        }
        return sortedArray;
    }

三、计数排序的优化

问题1:
        对于上面的计数排序,我们不难发现存在这样一个问题:如果要排序的20个随机整数的范围在81~93之间,那么我们就需要建立一个长度为94的数组,这样就会造成新建的数组的前面81个空间位置就白白浪费了。
解决方法:
        我们可以通过使用使用数列的最大值-最小值+1作为统计数组的长度即可,同时使用最小值作为一个偏移量,用于计算整数在统计数组中的下标。
问题2:
        以上的计数排序只是按照输入数列从小到大的一个顺序对其进行输出而已,并没有对原始数列进行排序。如果遇到类似于对学生分数进行排序这样的现实问题,比如对于相同分数的学生就会造成分不清分数对应的是谁这样的问题。
解决方法:
        假如现在有5个学生的分数为:(学生A:90,学生B:99,学生C:95,学生D:94,学生E:95),使用上面的方法得到的统计数组如下所示:
计数排序及其优化_第4张图片
        我们首先对统计数组进行变形,即从统计数组的第2个元素开始,每个元素都加上前面的所有元素之和,得到的新的统计数组如下所示:
计数排序及其优化_第5张图片
        然后,我们从后向前依次遍历5位学生的分数,第1个为学生E95分,找到统计数组下标为5的元素,值是4,代表学生E成绩排名在第4位,然后对数组下标为5的元素的值-1,表示下次再遇到成绩为95分的,其位置就会在第3位,按照此方法就会得出最终的成绩排名顺序为:(学生A:90,学生B:94,学生C:95,学生D:95,学生E:99)。
优化后的计数排序的实现:

public static int[] countSortUpdated(int[] array){
        //得到数列的最大值和最小值,并算出差值d
        int max=array[0];
        int min=array[0];
        for (int i=0;i<array.length;i++){
            if (array[i]>max){
                max=array[i];
            }
            if (array[i]<min){
                min=array[i];
            }
        }
        int d=max-min;
        //创建统计数组并统计对应元素的个数
        int[] countArray=new int[d+1];
        for (int i=0;i<array.length;i++){
            countArray[array[i]-min]++;
        }
        //统计数组做变形,后面的元素等于前面的元素之和
        for (int i=1;i<countArray.length;i++){
            countArray[i]+=countArray[i-1];
        }
        //倒序遍历原始数列,从统计数组中找出正确的位置,输出到结果数组
        int[] sortedArray=new int[array.length];
        for (int i=array.length-1;i>=0;i--){
            sortedArray[countArray[array[i]-min]-1]=array[i];
            countArray[array[i]-min]--;
        }

        return sortedArray;
    }

注:以上内容均为《漫画算法》所学。

你可能感兴趣的:(计数排序及其优化)