目录
前言:
计数排序
1.算法描述
2. 基本思想
3.实现逻辑
4.示例剖析
5.动图演示
代码实现
1.C/C++代码
2.Python代码
算法分析
时间复杂度
空间复杂度
稳定性
局限性
前言:
有没有一种排序时间复杂度为直线正比的排序算法呢?有当然有,那就是计数排序,那为什么时间复杂度如此小的排序算法,然而其排序速度却不如快速排序(nlogn)呢?这里我们就会想到可能会有另一种代价在付出的,是的,那就是要付出空间资源的代价,下面我们就一起来看看吧!
计数排序
1.算法描述
计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 [1] 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)
2. 基本思想
计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),然后进行分配、收集处理:
① 分配。扫描一遍原始数组,以当前值-minValue作为下标,将该下标的计数器增1。
② 收集。扫描一遍计数器数组,按顺序把值收集起来。
3.实现逻辑
① 找出待排序的数组中最大和最小的元素
② 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
③ 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
④ 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
4.示例剖析
假设这里有一个数组 [2,1,5,1,3,8,4,5,6,10,7] 如何通过计数排序来去实现排序呢?
第一步,先找到这个数组的最大值max元素,为10,然后创建一个数组count[max+1],即count[11],把count里面的数字都初始化为0,如下所示:
count [0,0,0,0,0,0,0,0,0,0,0]
第二步,开始遍历原数组,然后统计里面元素的数量,在对应位置count数组里面进行+1操作。比如原数组中1出现了两次,那么就在count[0]加上2,即count[0]=2。count就依次去统计原数组元素出现的次数,统计如下所示:
1 2 3 4 5 6 7 8 9 10
count 2 1 1 1 2 1 1 0 0 1 (这些是统计到出现的次数)
第三步,依次填入覆盖到原数组,最终得到排序后的数组
1 1 2 3 4 5 5 6 7 10
5.动图演示
代码实现
1.C/C++代码
#include<stdio.h>
#include<string.h>
//获取最大值
int get_max(int* n,int length) {
int max = n[0];
for (int i = 1; i < length; i++) {
if (n[i]>max)
max = n[i];
}
return max;
}
//计数排序
void count_sort(int* n, int max, int length) {
int index=0;
int range =max+1;
int* temp_arr = (int*)malloc(sizeof(int) * range);
memset(temp_arr, 0, sizeof(int) * range); //初始化全部都为0
for (int j = 0; j < length; j++) { //统计
temp_arr[n[j]]++;
}
for (int k = 0; k < range; k++) {
while (temp_arr[k]) {
n[index++]=k;//把统计后的数字覆盖给原数组上
temp_arr[k]--;
}
}
free(temp_arr);//释放空间
}
int main() {
int array[11] = { 2,1,5,1,3,8,4,5,6,10,7 };
printf("排序前:");
for (int i = 0; i < sizeof(array) / sizeof(int); i++) {
printf("%d ", array[i]);
}
printf("\n排序后:");
count_sort(array, get_max(array, sizeof(array) / sizeof(int)),sizeof(array) / sizeof(int));
for (int i = 0; i < sizeof(array) / sizeof(int); i++) {
printf("%d ", array[i]);
}
}
//排序前:2 1 5 1 3 8 4 5 6 10 7
//排序后:1 1 2 3 4 5 5 6 7 8 10
2.Python代码
import random as r
def count_sort(li,max):
length=len(li) #获取长度
index=0
new_li=[0 for _ in range(max)] #进入到统计
for i in li:
new_li[i-1]+=1
for j in range(0,len(new_li)): #覆盖原数组
while new_li[j]:
li[index]=j+1
index+=1
new_li[j]-=1
return li
if __name__ == '__main__':
li=[r.randint(1,10) for _ in range(10)]
print(li)
sort_li=count_sort(li,max(li))
print(sort_li)
# [3, 6, 6, 7, 7, 1, 9, 10, 8, 5]
# [1, 3, 5, 6, 6, 7, 7, 8, 9, 10]
算法分析
时间复杂度
O(n+k)
空间复杂度
O(k)
稳定性
稳定
局限性
注意:计数排序是需要去开辟一个空间内存的来存放统计数字的,如果当这个要进行排序的数组非常大的时候,这时候需要去申请的空间就会非常大,这很浪费空间的,所以选择的时候要慎用。
以上就是今天的全部内容了,我们下次见!
分享一张壁纸: