2-计数排序
算法思想:
计数排序最核心的思想就是计数序列中每个元素出现的次数,我们将每个元素的数量都记录下来之后.我们就可以通过按
了解完计数排序的基本思想之后,我们就来看看看这个算法的实现步骤又是怎么样的呢?主要就是下面这几个步骤:
1.第一次遍历序列,找出序列中的最大值以及最小值,然后根据最大值MAX与最小值MIN创建一个MAX-MIN+1长度的数组.为什么创建这样长度的数组呢,因为只有创建了这样长度的数组,MIN-MAX区间内的每个元素才有对应的位置进行存放,如下图所示:
2.第二次遍历序列,我们每次遍历一个元素都将该元素所对应的区间数组对应的位置进行+1操作,这个步骤其实就是我们计数排序的核心----计数了.遍历结束之后,区间数组中的元素值就代表相应元素出现的次数,如下图所示:
3.最后一步就只需要按照区间数组中的次数一次将该元素打印出来即可.如下图所示:
计数排序的基本思想基本就是这样,按照惯例,还是通过下面的图来帮助大家更好的理解计数排序的基本思想:
了解完计数排序的基本思想之后,我们还是按照惯例分析一下计数排序算法的一些特点:
-计数排序是稳定的 ,这个大家应该能很明显的看出来,因为计数排序本身并不是基于比较的算法.
-计数排序需要的额外空间比较大,这个大家很明显的看出来,并且空间浪费的情况也会比较严重,因为一旦序列中MAX与MIN的差距过大,那么需要的内存空间就会非常大.并且假如序列中的元素都是散布在一个特定的区间内,那么内存空间浪费的情况也会非常明显.
算法图解:
示例代码:
public static void main(String[] args) { int []num ={7,4,9,3,2,1,8,6,5,10}; long startTime=System.currentTimeMillis(); int min=Integer.MAX_VALUE; int max=Integer.MIN_VALUE; //先找出数组中的最大值与最小值 for(int i=0;i<num.length;i++) { if(num[i]<min) min=num[i]; if(num[i]>max) max=num[i]; } //创建一个长度为max-min+1长度的数组来进行计数 int []figure=new int [max-min+1]; for(int i=0;i<num.length;i++) { //计算每个数据出现的次数 figure[num[i]-min]++; } int begin=0; //创建一个新的数组来存储已经排序完成的结果 int []num1=new int [num.length]; for(int i=0;i<figure.length;i++) { //循环将数据pop出来 if(figure[i]!=0) { for(int j=0;j<figure[i];j++) { num1[begin++]=min+i; } } } System.out.println("数据范围:"+min+"~"+max); System.out.println("计数结果: "); for(int i=0;i<num.length;i++) System.out.println(" "+num[i]+"出现"+figure[num[i]-min]+"次"); System.out.print("排序结果: "); for(int i=0;i<num1.length;i++) System.out.print(num1[i]+" "); System.out.println(); long endTime=System.currentTimeMillis(); System.out.println("程序运行时间: "+(endTime-startTime)+"ms"); }
复杂度分析:
理解完计数排序的基本思想之后,我们就需要来分析一下他的时间复杂度,空间复杂度.
时间复杂度
计数排序很明显是一种通过空间来换时间的算法,因为我们可以很明显的看到计数排序需要三次遍历,两次遍历我们的原序列,第三次是遍历我们的区间数组.那么很明显时间复杂度一定是线性级别的但是因为第三次遍历的并不是我们的原序列,而是我们的区间数组,所以时间复杂度并不是我们的平常的O(n),而是应该加上我们遍历区间数组的时间,假设我们的区间数组长度为k的话,那么我们的时间复杂度就是O(n+k)
空间复杂度
上面我们已经说过了,计数排序本身就是一个通过空间来换取时间的算法,所以很明显他的空间复杂度就会很高.并且这个空间复杂度主要就取决于我们区间数组的长度,所以假设我们的区间数组长度为k的话,那么我们的空间复杂度就为O(k)