小学生图解排序算法:⑧计数排序

计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。当然这是一种牺牲空间换取时间的做法。

其基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。

图解计数排序
文字说明比较抽象,依然直接先上图举例来分析一下其过程。
小学生图解排序算法:⑧计数排序_第1张图片

图解说明

根据图解过程发现,我们采用计数排序需要知道一些前提条件。

例如数组中的最大值与最小值。所以写代码时,显然这个是需要作为形参手动传入的。

当然,也有人想,我何必用肉眼去观察呢,可以用其它排序手法写个程序找出其最大值最小值,再传入此处啊。认真思考一下,这样多没意思啊,其它排序手法直接就可以排好序啊,真这样做又何必多此一举再用计数排序呢。

所以,这里其实引申出了计数排序的使用场景。就是无论数列有多少元素,其最大值最小值应当容易辨别。例如高中生考试的总分数,显然用0-750就OK啦;又比如一群人的年龄,用个0-150应该就可以了,再不济就用0-200喽。

除了最值容易辨别外,还有个暗含条件是,最大值最小值不能相差太大,否则需要消耗很多的资源。例如数组{1,20000000,5,8},最大值最小值容易辨别,但!如果用计数排序,那么新建计数数组的长度是2千万,显然这种场景不应该采用计数排序。

核心代码
根据图解及其说明,我们可以尝试写出计数排序的核心代码。

public static void countSort(int[] a, int max, int min) {
   int[] b = new int[a.length];//存储数组
   int[] count = new int[max - min + 1];//计数数组
   int[] sum = new int[max - min + 1];//加总数组

   for (int num = min; num <= max; num++) {
      count[num - min] = 0;//初始化各元素值为0,数组下标从0开始因此减min
   }

   for (int i = 0; i < a.length; i++) {
      int num = a[i];
      count[num - min]++;//每出现一个值,计数数组对应元素的值+1
   }

   sum[0] = count[0];
   for (int num = min + 1; num <= max; num++) {
      sum[num - min] = sum[num - min - 1] + count[num - min];//加总数组元素的值为计数数组对应元素及左边所有元素的值的总和
   }

   for (int i = 0; i < a.length; i++) {
      int num = a[i];//源数组第i位的值
      int index = sum[num - min] - 1;//加总数组中对应元素的下标
      b[index] = num;//将该值存入存储数组对应下标中
      sum[num - min]--;//加总数组中,该值的总和减少1。
   }

   for(int i=0;i//将存储数组的值一一替换给源数组
       a[i] = b[i];
   }
}

调用以上代码:

   int[] a = new int[]{6, -2, -8, 9, 3};
   countSort(a, 9, -8);//9和-8分别是最大值和最小值。

说明

本文为个人学习笔记,如有细节错误或描述歧义,请留言告知,谢谢!
本文首发于博客专栏:http://Windows9.Win/count_sort_algorithm

你可能感兴趣的:(算法Algorithm)