计数排序
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
计数排序 (Counting sort) 是一种稳定的排序算法。计数排序使用一个额外的数组 C
,其中第 i
个元素是待排序数组 A
中值等于 i
的元素的个数。然后根据数组 C
来将 A
中的元素排到正确的位置。它只能对整数进行排序
计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 [1] 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(nlog(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(nlog(n)), 如归并排序,堆排序)
算法步骤
- 找出数组中的最大值
max
、最小值min
; - 创建一个新数组
C
,其长度是max-min+1
,其元素默认值都为 0; - 遍历原数组
A
中的元素A[i]
,以A[i]-min
作为C
数组的索引,以A[i]
的值在A
中元素出现次数作为C[A[i]-min]
的值; - 对
C
数组变形,新元素的值是该元素与前一个元素值的和,即当i>1
时C[i] = C[i] + C[i-1]
; - 创建结果数组
R
,长度和原始数组一样。 - 从后向前遍历原始数组
A
中的元素A[i]
,使用A[i]
减去最小值min
作为索引,在计数数组C
中找到对应的值C[A[i]-min]
,C[A[i]-min]-1
就是A[i]
在结果数组R
中的位置,做完上述这些操作,将count[A[i]-min]
减小 1。
基本实现
Golang版
func getMinMax(data []int) (min, max int) {
min, max = data[0], data[0]
for i := 1; i < len(data); i++ {
if min > data[i] {
min = data[i]
}
if max < data[i] {
max = data[i]
}
}
return min, max
}
func countSort(data []int) int {
min, max := getMinMax(data)
m := make([]int, max-min+1)
result := make([]int, len(data))
for i := 0; i < len(data); i++ {
m[data[i]-min] += 1
}
for i := 1; i < len(m); i++ {
m[i] += m[i-1]
}
for i := len(data) - 1; i >= 0; i-- {
idx := m[data[i]-min] - 1
result[idx] = data[i]
m[data[i]-min] -= 1
}
return result
}
Rust版
pub fn main() {
let data = vec![1, 4, 5, 7, 4, 6, 3];
println!("{:?}", count_sort(&data));
}
fn count_sort(arr: &[i32]) -> Vec<i32> {
// 找到最大值和最小值
let min = arr.iter().min().unwrap();
let max = arr.iter().max().unwrap();
// 初始化计数数组并将所有元素都设置为0
let mut count = vec![0; (max - min + 1) as usize];
// 遍历数组并对每个元素出现次数进行计数
for &num in arr {
count[(num - min) as usize] += 1;
}
// 生成排序后的结果数组
let mut result = vec![0; arr.len()];
let mut index = 0;
for i in 0..count.len() {
for _ in 0..count[i] {
result[index] = i as i32 + min;
index += 1;
}
}
result
}