容易被忽略的计数排序稳定性
计数排序中,一个容易被忽略的点是保持原数组元素的相对顺序,即排序算法的稳定性。在下面的实现中,数组中每个元素被计数后,被进行无序的还原。
func CountingSortNaive(nums []int, N int) {
counting := make([]int, N)
for _, num := range nums {
counting[num] = counting[num] + 1
}
k := 0
for i, count := range counting {
for count > 0 {
nums[k] = i
k = k + 1
count = count - 1
}
}
}
为了保持稳定性,我们在排序前对计数数组做一个滚动累加,以计算出每个值在原数组中对应的下标,最后对原数组从后向前做排序。
func CountingSort(nums []int, N int) {
counting := make([]int, N)
tmp := make([]int, len(nums))
for i := range nums {
counting[nums[i]] = counting[nums[i]] + 1
}
for i := range counting {
counting[i] = counting[i] + counting[i-1]
}
for i := len(nums) - 1; i >= 0; i = i - 1 {
v := nums[i]
tmp[counting[v]-1] = v
counting[v] = counting[v] - 1
}
for i := 0; i < len(tmp); i = i + 1 {
nums[i] = tmp[i]
}
}
稳定的计数排序能帮助处理基数排序的问题。