计数排序

241 阅读4分钟

什么是计数排序

计数排序(Counting sort)是一种稳定的线性时间排序算法。该算法于1954年由 Harold H. Seward 提出。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值小于i的元素的个数。然后根据数组C 来将A中的元素排到正确的位置。

计数排序的特点

计数排序特点:

  • 从来不会对排序元素进行比较
  • 对排序数组的元素不能是小数、或者字符串,只能是整数
  • 以空间换时间(空间:内存)
  • 时间复杂度为O(n + m)(ps:n数组元素的个数,m数组元素的取值范围)
  • 空间复杂度O(n + m)(ps:n数组元素的个数,m数组元素的取值范围)

什么时候选择使用排序算法呢?

主要到之前提到的空间复杂的为O(n + m),如果m的取值范围过大的话会造成内存压力过大,假设m为0~10000,这样我们在开辟临时数组时,需要开辟一个空间为10000的数组,所以在m过大时一般不提倡使用计数排序,我们一般在m在100以内去使用计数排序(个人经验),当然在开发过程中还是需要具体情况具体分析。

计数排序中心思想

当输入的元素是n个0 到 m 之间的整数时,它的运行时间是O(n+m)。计数排序不是比较排序,排序的速度快于任何比较排序算法。
核心思想:统计每个整数在序列中出现的次数,统计每个整数比自身小的数的个数,进而推导出每个整数在有序序列中的索引

  • 由于用来计数的数组C 的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序算法中,能够更有效的排序数据范围很大的数组。
  • 通俗的理解:例如有10个年龄不同的人,统计出有8个人的年龄比A小,那A的年龄就排在第9位,用这个方法可以得到其他每个人的位置,也就排好了序。

算法基本步骤图解

废话少说,上代码

var A:[Int] = [1,9,10,30,50,-43,6,-2,10,-3,99]

//MARK:计数排序
func countingSort(_ arr:[Int]) -> [Int]
{
    //1.找出待排序的数组中最大和最小的元素
    let maxNum:Int = arr.max()!
    let minNum:Int = arr.min()!
    
    //初始化一个与arr同样大小的数组
    var b:[Int] = [Int](repeating: 0, count: arr.count)
    //k的大小是要排序的数组中最大值和最小值之差 + 1
    let k:Int = maxNum - minNum + 1
    var c:[Int] = [Int](repeating: 0, count: k)
    //2.统计数组中每个值为的元素出现的次数,存入数组的第项
    for i in 0..<arr.count
    {
        //优化减小数组的大小,统计每个元素有几个
        // c[arr[i] - minNum]:原数组中的最大元素出现的次数,存放在c数组的最后一位
        c[arr[i] - minNum] += 1
    }
    //3.对所有的计数累加(从中的第一个元素开始,每一项和前一项相加)
    for i in 1..<c.count
    {
        //下标i:存放原数组中比i小的数的数量
        //前缀和
        c[i] += c[i-1]
    }
    //4.反向填充目标数组
    for i in (0...arr.count - 1).reversed()
    {
        //按存取的方式取出c的元素
        //数量减1
        c[arr[i] - minNum]  -= 1
        //这个数量 代表了在新数组中的下标
        b[c[arr[i] - minNum]] = arr[i]
    }
    return b
}
var b = countingSort(A)
for ele in b {
    print(ele)
}

代码步骤解析:

  • 1.找出待排序的数组中最大和最小的元素
  • 2.统计数组中每个值为i的元素出现的次数,存入数组c 的第i项
  • 3.对所有的计数累加 (从c 中的第一个元素开始,每一项和前一项相加)
  • 4.反向填充目标数组:将每个元素i放在新数组(b)的第c[arr[i] - minNum]项,每放一个元素就将c[arr[i] - minNum]减去1

扩展
如何对字符串等进行排序呢?下一篇我们会介绍基数排序