每日一算法|计数排序---第九天

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]


以上是关于每日一算法|计数排序---第九天的主要内容,如果未能解决你的问题,请参考以下文章

每日算法&面试题,大厂特训二十八天——第九天(位运算)

冲刺大厂每日算法&面试题,动态规划21天——第九天

每日算法计数&基数&桶&位图排序-简介

每日一算法|直接插入排序---第五天

每日一算法|基数排序---第十一天

C语言每日一练——第161天:冒泡排序算法