在计算机科学中,排序是一门基础的算法技术,许多算法都要以此作为基础,不同的排序算法有着不同的时间开销和空间开销。排序算法有非常多种,如我们最常用的快速排序和堆排序等算法,这些算法需要对序列中的数据进行比较,因为被称为基于比较的排序。
基于比较的排序算法是不能突破O(NlogN)的。简单证明如下:
N个数有N!个可能的排列情况,也就是说基于比较的排序算法的判定树有N!个叶子结点,比较次数至少为log(N!)=O(NlogN)(斯特林公式)。
而非基于比较的排序,如计数排序,桶排序,和在此基础上的基数排序,则可以突破O(NlogN)时间下限。但要注意的是,非基于比较的排序算法的使用都是有条件限制的,例如元素的大小限制,相反,基于比较的排序则没有这种限制(在一定范围内)。但并非因为有条件限制就会使非基于比较的排序算法变得无用,对于特定场合有着特殊的性质数据,非基于比较的排序算法则能够非常巧妙地解决。
基于非比较的排序算法有三种,计数排序,桶排序和基数排序。
-----------------------------我是分割线-------------------------------------------------------
1. 计数排序
计数排序(Counting sort)是一种稳定的线性时间排序算法。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。
特征:
当输入的元素是n个0到k之间的整数时,它的运行时间是Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。
由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序算法中,能够更有效的排序数据范围很大的数组。
通俗地理解,例如有10个年龄不同的人,统计出有8个人的年龄比A小,那A的年龄就排在第9位,用这个方法可以得到其他每个人的位置,也就排好了序。当然,年龄有重复时需要特殊处理(保证稳定性),这就是为什么最后要反向填充目标数组,以及将每个数字的统计减去1的原因。算法的步骤如下:
- 找出待排序的数组中最大和最小的元素
- 统计数组中每个值为i的元素出现的次数,存入数组 C 的第 i 项
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
java 实现:
1 public class CountingSort { 2 public static void main(String[] argv) { 3 int[] A = CountingSort.countingSort(new int[]{16, 4, 10, 14, 7, 9, 3, 2, 8, 1}); 4 Utils.print(A); 5 } 6 7 public static int[] countingSort(int[] A) { 8 int[] B = new int[A.length]; 9 // 假设A中的数据a'有,0<=a' && a' < k并且k=100 10 int k = 100; 11 countingSort(A, B, k); 12 return B; 13 } 14 15 private static void countingSort(int[] A, int[] B, int k) { 16 int[] C = new int[k]; 17 // 计数 18 for (int j = 0; j < A.length; j++) { 19 int a = A[j]; 20 C[a] += 1; 21 } 22 Utils.print(C); 23 // 求计数和 24 for (int i = 1; i < k; i++) { 25 C[i] = C[i] + C[i - 1]; 26 } 27 Utils.print(C); 28 // 整理 29 for (int j = A.length - 1; j >= 0; j--) { 30 int a = A[j]; 31 B[C[a] - 1] = a; 32 C[a] -= 1; 33 } 34 } 35 } 36 37 38 //针对c数组的大小,优化过的计数排序 39 public class CountSort{ 40 public static void main(String []args){ 41 //排序的数组 42 int a[] = {100, 93, 97, 92, 96, 99, 92, 89, 93, 97, 90, 94, 92, 95}; 43 int b[] = countSort(a); 44 for(int i : b){ 45 System.out.print(i + " "); 46 } 47 System.out.println(); 48 } 49 public static int[] countSort(int []a){ 50 int b[] = new int[a.length]; 51 int max = a[0], min = a[0]; 52 for(int i : a){ 53 if(i > max){ 54 max = i; 55 } 56 if(i < min){ 57 min = i; 58 } 59 } 60 //这里k的大小是要排序的数组中,元素大小的极值差+1 61 int k = max - min + 1; 62 int c[] = new int[k]; 63 for(int i = 0; i < a.length; ++i){ 64 c[a[i]-min] += 1;//优化过的地方,减小了数组c的大小 65 } 66 for(int i = 1; i < c.length; ++i){ 67 c[i] = c[i] + c[i-1]; 68 } 69 for(int i = a.length-1; i >= 0; --i){ 70 b[--c[a[i]-min]] = a[i];//按存取的方式取出c的元素 71 } 72 return b; 73 } 74 }