计数排序&基数排序&改进

648 阅读2分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

计数排序

前言:

这是一种不需要比较的排序方式,但极其浪费空间。(写嵌入式的小伙伴会气死吧)

原理:

统计每个整数在序列中出现的次数,进而推导出每个整数在有序序列中的索引

1628443378045-18531bd2-4697-4e8a-908c-c2da8ff65033.png

示例:

//这是最简单的实现方式
 
// 找出最大值
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
        } // O(n)
        
        // 开辟内存空间,存储每个整数出现的次数
        int[] counts = new int[1 + max];
        // 统计每个整数出现的次数
        for (int i = 0; i < array.length; i++) {
            counts[array[i]]++;
        } // O(n)
        
        // 根据整数的出现次数,对整数进行排序
        int index = 0;
        for (int i = 0; i < counts.length; i++) {
            while (counts[i]-- > 0) {
                array[index++] = i;
            }
        } // O(n)

算法分析:

这个版本的实现存在以下问题:无法对负整数进行排序;极其浪费内存空间;是个不稳定的排序 。

(用这个的人就很神奇)

计数排序 – 改进思路

原本我们是把数组的索引作为记录原序列中的数值,但是数组中没有负数,所以我们就没有办法比较序列中的负数。

现在我们将数组中第一个位置就是序列中最小的数,利用这个数和数组索引的差,

1628502501716-0b0fab4a-528f-4371-8956-5911b2b69299.png

解决的不能负数的问题,现在来解决不稳定

1628504775294-927e1ec9-5d2f-4616-9136-aef799f1bb1f.png 假设array中的最小值是 min; array中的元素 k 对应的 counts 索引是 k – min; array中的元素 k 在有序序列中的索引; counts[k – min] – p; p 代表着是倒数第几个 k;

1628504893826-410f34bf-89fe-4f9c-a60c-c38162ecd423.png

1628504906903-6b638877-9008-431e-92d8-9497c162ded5.png

// 找出最值
        int max = array[0];
        int min = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
            if (array[i] < min) {
                min = array[i];
            }
        }
        
        // 开辟内存空间,存储次数
        int[] counts = new int[max - min + 1];
        // 统计每个整数出现的次数
        for (int i = 0; i < array.length; i++) {
            counts[array[i] - min]++;
        }
        // 累加次数
        for (int i = 1; i < counts.length; i++) {
            counts[i] += counts[i - 1];
        }
        
        // 从后往前遍历元素,将它放到有序数组中的合适位置
        int[] newArray = new int[array.length];
        for (int i = array.length - 1; i >= 0; i--) {
            newArray[--counts[array[i] - min]] = array[i];
        }
        
        // 将有序数组赋值到array
        for (int i = 0; i < newArray.length; i++) {
            array[i] = newArray[i];
        }
    }

基数排序

前言:

基数排序,所谓的基数就是他的个位,十位,百位,通过个十百千的顺序依次排列各位的数字大小,最后得到所有数字的大小。

原理:

基数排序非常适合用于整数排序(尤其是非负整数),因此只演示对非负整数进行基数排序 执行流程:依次对个位数、十位数、百位数、千位数、万位数...进行排序(从低位到高位)

1628525299836-3c0692cc-ae6d-4646-b9e6-2799e1b80f75.png

对位数排序时,我们使用计数排序

示例:

// 找出最大值
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
        }
        
        // 个位数: array[i] / 1 % 10 = 3
        // 十位数:array[i] / 10 % 10 = 9
        // 百位数:array[i] / 100 % 10 = 5
        // 千位数:array[i] / 1000 % 10 = ...
​
        for (int divider = 1; divider <= max; divider *= 10) {
            countingSort(divider);
        }
​
protected void countingSort(int divider) {
        // 开辟内存空间,存储次数
        int[] counts = new int[10];
        // 统计每个整数出现的次数
        for (int i = 0; i < array.length; i++) {
            counts[array[i] / divider % 10]++;
        }
        // 累加次数
        for (int i = 1; i < counts.length; i++) {
            counts[i] += counts[i - 1];
        }
        
        // 从后往前遍历元素,将它放到有序数组中的合适位置
        int[] newArray = new int[array.length];
        for (int i = array.length - 1; i >= 0; i--) {
            newArray[--counts[array[i] / divider % 10]] = array[i];
        }
        
        // 将有序数组赋值到array
        for (int i = 0; i < newArray.length; i++) {
            array[i] = newArray[i];
        }
    }

基数排序 – 另一种思路

我们先按照个位数的大小竖着放起来

1628526998097-1d4fac61-723a-4ef6-bf58-5d56b4405973.png

然后,我们依次将数穿起来,最后,就会把数列排好

示例:

// 找出最大值
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] > max) {
                max = array[i];
            }
        }
        //用来记录的二维数组
        int buckets[][]=new int[10][array.length];
        //二维数组每列的数量
        int bucketSizes[]=new int[buckets.length];
​
        for (int divider = 1; divider <= max; divider *= 10){
            for (int i=0;i<array.length;i++){
                int a=array[i] / divider % 10;
                buckets[a][bucketSizes[a]++]=array[i];
            }
            int index=0;
            for (int i=0;i<buckets.length;i++){
                for (int j=0;j<bucketSizes[i];j++){
                    array[index++]=buckets[i][j];
                }
                bucketSizes[i]=0;
            }
        }