计数排序:时间复杂度仅为 O(n) 的排序算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/afei__/article/details/82959924

一、简介

计算排序假设 n 个输入元素都是 0 到 k 区间内的一个整数,其中 k 为某个整数。

基本原理:

创建一个长度为 k+1 的数组 count[],它的 count[i] 的值对应输入数组中 i 出现的次数。通过遍历一次输入数组并统计每个元素出现次数,最后遍历 count[] 输出。

 

二、时间复杂度

计算排序的时间复杂度为 O(n)。

计算排序不是比较排序算法,它没有元素之间的比较操作(判断大于或者小于),而我们之前所了解到的所有的比较排序算法的时间复杂度下界均为 Ω(n * lg n)。

 

三、实现

public class Main {

    public static void main(String[] args) {
        // 输入元素均在 [0, 10) 这个区间内
        int[] arr = new int[] { 5, 4, 6, 7, 5, 1, 0, 9, 8, 1 };
        countSort(arr);
        printArray(arr);
    }

    public static void countSort(int[] arr) {
        int[] count = new int[10];
        for (int i = 0; i < arr.length; i++) {
            count[arr[i]]++; // 计数每个元素出现次数
        }
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0) {
                arr[index++] = i;
                count[i]--;
            }
        }
    }

    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

}

运行结果:

0 1 1 4 5 5 6 7 8 9 

 

四、扩展

前面我们看到,计数排序的前提是输入元素都是在 [0, k] 这样的一个区间内。那么假如我们存在负数的输入,或者我们也不确定输入元素的具体范围是多少,又该怎么变通呢?

其实,以上情况也不难解决。我们只需要多遍历一次数组,找到元素的最大值 max 和最小值 min,然后依旧创建一个长度为 max - min + 1 长度的数组,其中 min 是可能为负数的。然后我们引入一个变量 offset 来修正数值 i 在计数数组 count[] 中的正确位置。

代码实现:

public class Main {

    public static void main(String[] args) {
        // 输入元素均在 [0, 10) 这个区间内
        int[] arr = new int[] { -3, 15, -12, 0, 48, 41, -8, 23, 33, 33 };
        countSort(arr);
        printArray(arr);
    }

    public static void countSort(int[] arr) {
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(max, arr[i]);
            min = Math.min(min, arr[i]);
        }
        int offset = 0 - min;
        int[] count = new int[max - min + 1];
        for (int i = 0; i < arr.length; i++) {
            count[arr[i] + offset]++; // 计数每个元素出现次数
        }
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0) {
                arr[index++] = i - offset;
                count[i]--;
            }
        }
    }

    public static void printArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

}

运行结果:

-12 -8 -3 0 15 23 33 33 41 48 

结果也是正确的。

 

五、总结

可以看到,计数排序是我们目前接触到的排序算法中,时间复杂度最低的,仅为 O(n)。

但其也存在一定的限制,它比较适合我们已知输入元素的取值范围的情况。

猜你喜欢

转载自blog.csdn.net/afei__/article/details/82959924
今日推荐