【声明】
非完全原创,部分内容来自于学习其他人的理论。如果有侵权,请联系我,可以立即删除掉。
一、计数排序
1、方法和复杂度
1.1、核心思想和方法
计数排序是一个非基于比较的排序算法,对输入的数据有附加的限制条件:
1、输入的线性表的元素属于有限偏序集S;
2、设输入的线性表的长度为n,|S|=k(表示集合S中元素的总数目为k),则k=O(n)。
在这两个条件下,计数排序的复杂性为O(n)。
计数排序的基本思想是对于给定的输入序列中的每一个元素x
,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x
直接存放到最终的输出序列的正确位置上。
1.2、稳定性
排序算法是稳定的,因为在确定最终有序序列而遍历原序列时,遍历的方式是自右往左,从而导致后面的元素一定是排在后面的(代码中会给出验证)
1.3、时间复杂度
时间复杂度为 O ( n + k ) O(n+k) O(n+k),其中n为要排序的元素个数,k为data中最大的整数加1
1.4、空间复杂度
空间复杂度为 O ( n + k ) O(n+k) O(n+k),其中n为要排序的元素个数,k为data中最大的整数加1
2、排序的过程
2.1、基于队列数组的方式
(1)初始化一个计数数组(若输入序列中的元素是整数,则数组的每一个元素存放的是序列中元素的计数值;若输入序列中的元素是其他类型,则数组的每一个元素存放的是一个队列),计数数组大小是输入序列的所有元素待排序字段中最大的数
(2)遍历输入序列,遇到一个元素,若输入序列是整数,则根据数值计数数组对应的位置上加一;若输入序列是其他类型,就根据元素待排序字段的值在计数数组中找到其索引,将元素放入到队列中
(3)依次从计数数组中取出元素,填充到数组中
如下图中,结构体元素的第一个字段为待排序字段(字段最大值是3),因此计数数组的长度为4(索引分别为0、1、2、3,由于2没有用到因此未画出来),每一个元素均为队列
2.2、基于偏移地址的方式
见:排序算法的C语言实现(下 线性时间排序:计数排序与基数排序)
3、算法实现
package main
import "fmt"
const size = 10
type Data struct {
No int
Name string
}
func Count(arr []*Data) {
var index int = 0
//记录输入序列中待排序字段的最大值
var tmp_len int = 0
for _, data := range arr {
if tmp_len < data.No {
tmp_len = data.No
}
}
tmp_len++
//创建数组,go不支持动态创建数组,因此用切片
//队列用[]Data切片来表示,因为可以根据索引做到先进先出
tmp_arr := make([][]*Data, tmp_len)
for _, data := range arr {
tmp_arr[data.No] = append(tmp_arr[data.No], data)
}
//按照数组的顺序取元素
for i := 0; i < tmp_len; i++ {
if tmp_arr[i] != nil {
for j := 0; j < len(tmp_arr[i]); j++ {
arr[index] = tmp_arr[i][j]
index++
}
}
}
fmt.Println("【Count】排序, 最终结果: ")
for i, data := range arr {
fmt.Printf("i = %d, (%p)%v\n", i, data, *data)
}
}
func main() {
arr := [size]*Data{
{