前言:
前面所讲的排序算法基本都是需要进行两个数依次比较,这种两个数依次比较的算法不依赖于数组重元素的特性并且有下界Ω(nlogn)。换句话说就是使用比较排序算法最快的时间消耗没法小于这个界。那么是不是我们永远没法跨越这个梗呢?答案当然不是,当数组中的元素有一定的特点的时候,我们就可以利用这个特定,以实现排序算法的时间消耗与n呈线性的关系。
特性一:数组中所有元素正负性一致并且他们绝对值都小于某一个数。
当数组中所有元素都为正数或者都为负数的时候其实比较的算法是一致。这里我们假设所有元素都是非负。关于这个特性我们的思路灵感可能来自于统计一段文字中每个字母出现的次数。我们可以假设数组中所有元素都小于k。那么我们可以建立一个长度为k的数组,通过遍历要排序的数组,我们可以知道元数组中特定值的元素的个数。更进一步的,完成第一步之后我们可以知道原数组中小于等于某一元素的个数。既然我们知道了小于该元素的个数,就很简单的能得到该元素应该在数组中的位置。 这种排序算法叫做计数排序(Counting Sort)。源代码如下:
1 public static void main(String[] args) { 2 int[] arr = { 1, 12, 11, 4, 5, 7, 3, 1, 23, 56, 34, 76, 25, 76 }; 3 countingSort(arr, 80); 4 } 5 6 private static void countingSort(int[] arr, int upBound/** 数组中元素的上界 */ 7 ) { 8 // 新建一个数组,该数组用来存储arr中所有值出现的次数(如果不给数组中的元素赋值,默认每个值都为0) 9 int[] valueCountArr = new int[upBound]; 10 11 // 计算arr中对应值出现的次数 12 for (int i = 0; i < arr.length; i++) { 13 valueCountArr[arr[i]] = valueCountArr[arr[i]] + 1; 14 } 15 16 // 使valueCountArr[i]中存储的为原数组中小于等于i的元素的个数 17 for (int i = 1; i < upBound; i++) { 18 valueCountArr[i] += valueCountArr[i - 1]; 19 } 20 21 // 新建一个数组用来存放排序完成之后的值 22 int[] sortedArr = new int[arr.length]; 23 for (int i = 0; i < arr.length; i++) { 24 sortedArr[valueCountArr[arr[i]] - 1] = arr[i]; // 如果小于等于arr[i]的值的个数为j,那么arr[i]在排序好的数组中的位置应该在arr[j-1] 25 valueCountArr[arr[i]] = valueCountArr[arr[i]] - 1; // 此时小于等于特定值的计数应该减一(如果存在同样大小的元素,需要把计数减一,不然两个值的位置会互相覆盖) 26 } 27 28 System.out.println(Arrays.toString(sortedArr)); 29 }