当前位置:   article > 正文

排序算法——计数排序详解_计数排序的时间复杂度

计数排序的时间复杂度

在排序的最终结果中,各元素的次序依赖于它们之间的比较。这类排序算法被称为比较排序。对于包含 n n n 个元素的输入序列来说,任何比较排序算法在最坏情况下都要经过至少 O ( n   l o g   n ) O(n\ log\ n) O(n log n) 次比较。

而一些排序算法是用运算而不是比较来确定排序顺序的,如计数排序、基数排序以及桶排序。因此,比较次数的下界 O ( n   l o g   n ) O(n\ log\ n) O(n log n) 对它们是不适用的。

下面介绍计数排序。

计数排序

计数排序假设 n n n 个输入元素中的每一个都是在 0 0 0 k k k 区间内的一个整数,其中 k k k 为某个整数。计数排序的时间复杂度为 O ( n + k ) O(n+k) O(n+k),当 k = O ( n ) k=O(n) k=O(n) 时,排序的时间复杂度为 O ( n ) O(n) O(n)

计数排序要求输入数据必须是整数,在实际工作中,当 k = O ( n ) k=O(n) k=O(n) 时,一般会采用计数排序。如果输入元素的范围不在 0 0 0 k k k 之内,那么可以根据元素的最大值和最小值将其映射到 0 0 0 k k k 之内。

计数排序的基本思想:对于每一个输入元素 x x x,确定小于 x x x 的元素个数。利用这一信息,就可以直接把 x x x 放到它在输出数组中的位置上了。例如,如果有 17 个元素小于 x x x, 则 x x x 就应该在第 18 个输出位置上。当有几个元素相同时,这一方案要略做修改,因为不能把他们放在同一个输出位置上。

算法执行过程中需要三个数组:
(1)数组 A A A,为要排序的输入数组,数组大小为 n n n
(2)数组 B B B,提供临时存储空间,用于存储小于该下标值的元素个数,数组大小为 k + 1 k+1 k+1
(3)数组 C C C,存放排序的输出,数组大小为 n n n
因此该算法的空间复杂度为 O ( n + k ) O(n + k) O(n+k),当 k = O ( n ) k=O(n) k=O(n) 时,空间复杂度为 O ( n ) O(n) O(n)

算法执行过程如下:
(1)根据待排序数组 A A A 中元素的最大值 m a x max max 和最小值 m i n min min,申请大小为 m a x − m i n + 1 max-min+1 maxmin+1 的数组 C C C
(2)扫描数组 A A A,记录每个不同的元素出现的次数,将其记录在数组 C C C 中。数组 C C C 的每一位 C [ i ] C[i] C[i] 就代表数组 A A A 中元素 i + m i n i+min i+min 出现的次数。可以发现,计数排序的该过程,其实就是将待排序集合 A A A 中的每个元素值本身大小作为下标,依次进行了存放。而此时的数组 C C C 就是为了确定每个元素值出现了几次。
(3)对数组 C C C 中的每个值从前向后进行累加,每个位置的值更新为加上前一个位置的值,此时数组 C C C 中每个元素 C [ i ] C[i] C[i] 表示待排序数组 A A A 中 小于等于该下标值 i i i 的元素个数。
(4)反向遍历待排序数组 A A A 来填充目标数组 B B B:将 A A A 中每个元素值 A [ j ] A[j] A[j] 放在新数组的第 C [ A [ j ] − m i n ] C[A[j]-min] C[A[j]min] 位,然后将 C [ A [ j ] − m i n ] C[A[j]-min] C[A[j]min] 减一。

在这里插入图片描述

用上图举例在输入数组 A A A 上的处理过程。其中 A A A 中的每一个元素都是在 0 0 0 5 5 5 范围内:
(a)首先扫描一遍数组 A A A,记录 A A A 中每个元素出现的次数,将该信息存储在数组 C C C 中, C C C 中每个下标就表示 A A A 中不同的元素值。如当前图中数组 C C C 所示;
(b)从前向后对计数进行累加,当前数组 C C C 中每个元素代表小于等于该下标的元素出现的次数;
(c~e)反向遍历输入数组 A A A,将其每个元素 A [ j ] A[j] A[j] 放在输出数组 B B B 的第 C [ A [ j ] ] C[A[j]] C[A[j]] 项,然后将 C [ A [ j ] ] C[A[j]] C[A[j]] 减一。图中表示该过程迭代了一次、两次和三次之后,输出数组 B B B 和辅助数组 C C C 的情况。其中数组 B B B 只有浅色阴影部分有元素值填充;
(f)最终排好序的输出数组 B B B

计数排序的一个重要性质就是它是稳定的:具有相同值的元素在输出数组中的相对次序相同。也就是说,对两个相同的数来说,在输入数组中先出现的数,在输出数组中在位于前面
计数排序的稳定性很重要的另一个原因是:计数排序经常会被用作计数排序算法的一个子过程。

虽然计数排序看上去很强大,但是它存在两大局限性:

  • 当数据最大值和最小值差距过大时,并不适用于计数排序。

    比如给定 20 个随机整数,范围在 0 到 1 亿之间,此时如果使用计数排序的话,就需要创建长度为 1 亿的数组,不但严重浪费了空间,而且时间复杂度也随之升高。
    因此计数排序只适用于元素值较为集中的情况。

  • 当数列元素不是整数时,并不适用于计数排序。

    如果数列中的元素都是小数,比如 3.1415,或是 0.00000001 这样子,则无法创建对应的统计数组,这样显然无法进行计数排序。

正是由于这两大局限性,才使得计数排序不像快速排序、归并排序那样被人们广泛适用。

Counting-sort(A, B) {
    maximum = max(A);
    minimum = min(A);
    int k = maximum - minimum + 1;

    let C[0..k] be a new array
    // initialion
    for i=0 to k            
        C[i] = 0;

    for j=0 to A.length - 1
        C[A[j]-minimum]++;
    // C[i] now contains the number of elements equal to i.

    for i=1 to k
        C[i] = C[i] + C[i-1]
    // C[i] now contains the number of elements less than or equal to i. 

    for j = A.length-1 downto 0
        B[C[A[j]-minimum]] = A[j];
        C[A[j]-minimum]--;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/913034
推荐阅读
相关标签
  

闽ICP备14008679号