每日一算法|计数排序---第九天
Posted 江小湖资源分享平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一算法|计数排序---第九天相关的知识,希望对你有一定的参考价值。
计数排序
计算出现的次数
(动图演示)
算法
原理
计数排序是一个非基于比较的排序算法(不需要进行比较)。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)。
算法的步骤如下:
(1)找出待排序的数组中最大和最小的元素
(2)统计数组中每个值为i的元素出现的次数,存入数组C的第i项
(3)对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
(4)反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
整个过程下来就跟打扑克牌整理手牌似的,发完手牌还是乱的,把手牌按顺序排好(有重复),再按从小到大打出去,顺序就排好了。
我们举个例子,假设有一组数列{3 ,1,5,1,2}
第一步:遍历数组找出最大值5和最小值1,开辟长度为(最大值-最小值+1)即5-1+1的空间,统计的个数就放在这个空间里。
第二步:统计每个数字出现的个数(按顺序)。可知1出现了2次,2出现1次,3出现1次,4出现0次,5出现1次,所以第一步开辟的空间中的数据为{2,1,1,0,1},
第三步:向原始数组中输入数据。首先是对第二步中开辟空间的数据进行遍历,第一位数字1有两个,向原始数组输出两个1;同理,依次输出一个2,一个3,零个4,一个5。这样,当个数数列全为0时,数据就排列成功了。
看完上面,是不是想问原本数组中为什么还有位置空间?
注意看,其实在第一步找出最大值和最小值之后,原数组中的数据就已经转移到新开辟的空间中了,也就是说,当完成第二步的时候原数组就已经是空的了,新建数组中存放了整个序列以及每个数字出现的次数。
C语言
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
//计数排序
void CountSort(int *a, int len)
{
assert(a);
//通过max和min计算出临时数组所需要开辟的空间大小
int max = a[0], min = a[0];
for (int i = 0; i < len; i++){
if (a[i] > max)
max = a[i];
if (a[i] < min)
min = a[i];
}
//使用calloc将数组都初始化为0
int range = max - min + 1;
int *b = (int *)calloc(range, sizeof(int));
//使用临时数组记录原始数组中每个数的个数
for (int i = 0; i < len; i++){
//注意:这里在存储上要在原始数组数值上减去min才不会出现越界问题
b[a[i] - min] += 1;
}
int j = 0;
//根据统计结果,重新对元素进行回收
for (int i = 0; i < range; i++){
while (b[i]--){
//注意:要将i的值加上min才能还原到原始数据
a[j++] = i + min;
}
}
//释放临时数组
free(b);
b = NULL;
}
//打印数组
void PrintArray(int *a, int len)
{
for (int i = 0; i < len; i++){
printf("%d ", a[i]);
}
printf("\n");
}
int main()
{
int a[] = { 3, 4, 3, 2, 1, 2, 6, 5, 4, 7 };
printf("排序前:");
PrintArray(a, sizeof(a) / sizeof(int));
CountSort(a, sizeof(a) / sizeof(int));
printf("排序后:");
PrintArray(a, sizeof(a) / sizeof(int));
system("pause");
return 0;
}
Java
public static void countingSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
int maxValue = Integer.MIN_VALUE;
int minValue = Integer.MAX_VALUE;
// 找出最大值与最小值
for(int i = 0; i < arr.length; i++){
if(arr[i] > maxValue) maxValue = arr[i];
else if(arr[i] < minValue) minValue = arr[i];
}
// 初始化计数数组
int[] counter = new int[maxValue - minValue + 1];
for(int i = 0; i < arr.length; i++){
counter[arr[i] - minValue]++;
}
// 计算每个元素的位置
for(int i = 0; i < counter.length - 1; i++){
counter[i + 1] += counter[i];
}
// 初始化一个新的数组存放排序后的元素
int[] ans = new int[arr.length];
for(int i = arr.length - 1; i >= 0; i--) {
ans[counter[arr[i] - minValue] - 1] = arr[i];
counter[arr[i] - minValue]--;
}
//将排序结果赋值到原始数组中
for(int i = 0; i < arr.length; i++){
arr[i] = ans[i];
}
}
Python
def countingSort(numList):
n = len(numList)
if n == 0 or n == 1:
return numList
maxVal = max(numList)
countArr = [0 for i in range(maxVal+1)]
for i in numList:
countArr[i] += 1
for i in range(1,len(countArr)):
countArr[i] += countArr[i-1]
res = [0 for i in range(n)]
for i in range(n-1,-1,-1):
res[countArr[numList[i]]-1] = numList[i]
countArr[numList[i]] -= 1
# 必须要减1,由于待排序元素在res中的位置是由计数数组的值来决定的。
# 当遍历了元素x之后,小于x的元素不会受影响,大于x的元素不会受影响,
# 只有等于x的元素会受影响,在往res中压的时候,要比x的位置往前移动一位,
# 因此需要将计数数组中的下标为x的值减1,使得下次在遍历到x的时候,
# 压入的位置在前一个x的位置之前
return res
numlist=[5,8,9,3,2,5,1,6,8]
print(countingSort(numlist))
# 输出结果为:[1, 2, 3, 5, 5, 6, 8, 8, 9]
以上是关于每日一算法|计数排序---第九天的主要内容,如果未能解决你的问题,请参考以下文章