算法思路:
利用空间来换时间的算法
1.找出数组arr最大值与最小值
2.构建一个长度为 [最大值-最小值+1] 的计数数组cnt,记录每个元素出现的次数,
索引:arr[i] - minNum
3. 自前往后,将 计数数组cnt 的元素存入原数组arr
平均时间复杂度:O(n + k)
n: 是输入数组长度,k是最大的数的大小
空间复杂度:O(k)
稳定性:稳定
适合场景:待排序序列是在一定范围内的整数。
代码实现一:
1.找出数组arr最大值与最小值
2.构建一个长度为 [最大值-最小值+1] 的计数数组cnt,记录每个元素出现的次数,
索引:arr[i] - minNum
4. 自前往后,将 计数数组cnt 的元素存入原数组arr
package main
import (
"fmt"
"math"
)
func main() {
arr := []int{3, 7, 6, 5, 4, 9, 6, 4, 4, 10}
countSort(arr)
for _, v := range arr {
fmt.Println(v)
}
}
func countSort(arr []int) {
maxNum, minNum := math.MinInt64, math.MaxInt64
for _, v := range arr {
maxNum = max(maxNum, v)
minNum = min(minNum, v)
}
cnt := make([]int, maxNum-minNum+1)
for i := 0; i < len(arr); i++ {
cnt[arr[i]-minNum]++
}
index := 0
for i := 0; i < len(cnt); i++ {
for cnt[i] > 0 {
arr[index] = i + minNum
cnt[i]--
index++
}
}
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
代码实现二:实现稳定性
优化:
将cnt数组转为前缀和数组:
自右向左遍历保证了算法的稳定性
通过前缀和数组找到每个元素排完序后对应的数组索引下标
package main
import (
"fmt"
"math"
)
func main() {
arr := []int{3, 7, 6, 5, 4, 9, 6, 4, 4, 10}
countSort(arr)
for _, v := range arr {
fmt.Println(v)
}
}
func countSort(arr []int) {
maxNum, minNum := math.MinInt64, math.MaxInt64
ans := make([]int, len(arr))
for _, v := range arr {
maxNum = max(maxNum, v)
minNum = min(minNum, v)
}
cnt := make([]int, maxNum-minNum+1)
for i := 0; i < len(arr); i++ {
cnt[arr[i]-minNum]++
}
// cnt数组 转为 前缀和数组:
for i := 1; i < len(cnt); i++ {
cnt[i] += cnt[i-1]
}
// arr数组 自后向前遍历:
// 计数数组下标 cntIndex
// 结果数组下标 ansIndex
// 存入ans后,计数数组 对应值 减一
for i := len(arr) - 1; i >= 0; i-- {
cntIndex := arr[i] - minNum
ansIndex := cnt[cntIndex] - 1
ans[ansIndex] = arr[i]
cnt[cntIndex]--
}
for i := 0; i < len(arr); i++ {
arr[i] = ans[i]
}
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}