今天挑战的内容是计数排序,这是一种不太常用的排序算法,因为太浪费内存空间了。
它的核心思想在于循环数组中的元素,以元素的值作为新数组的长度,统计相同元素的值出现的次数,最后根据新数组里有值的数据以及其下标进行排序。
先看看伪码
1 2 3 4 5 6 7 8 9 10 11 12 13 create key (counting) array for each element in list increase the respective counter by 1 for each counter, starting from smallest key while counter is non-zero restore element to list decrease counter by 1
代码实现 javascript版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function MyCountingSort ( ) { this .sort = function (array ) { var keyArray = []; for (var i = 0 ; i < array.length; i++) { var key = array[i]; if (keyArray[key]) { keyArray[key] += 1 ; } else { keyArray[key] = 1 ; } } var finalArray = []; for (var j = 0 ; j < keyArray.length; j++) { if (keyArray[j]) { while (keyArray[j] > 0 ) { finalArray.push(j); keyArray[j] -= 1 ; } } } return finalArray; } } var sorter = new MyCountingSort();var list = [2 ,5 ,2 ,5 ,9 ,2 ];var newlist = sorter.sort(list);console .log(newlist);
ruby版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 def my_counting_sort (array) keyArray = [] (array.size - 1 ).times do |i| key = array[i] if keyArray[key] keyArray[key] += 1 else keyArray[key] = 1 end end finalArray = [] (keyArray.size).times do |j| if keyArray[j] while keyArray[j] > 0 finalArray.push(j) keyArray[j] -= 1 end end end finalArray end list = [2 ,5 ,2 ,5 ,9 ,2 ] newlist = my_counting_sort(list) print newlist, "\n"
Java版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class MyCountingSort { public static void main (String []args) { int a[] = {2 ,5 ,2 ,5 ,9 ,2 }; int b[] = countSort(a); for (int i : b){ System.out.print(i + " " ); } System.out.println(); } public static int [] countSort(int []a){ int b[] = new int [a.length]; int max = a[0 ], min = a[0 ]; for (int i : a){ if (i > max){ max = i; } if (i < min){ min = i; } } int k = max - min + 1 ; int c[] = new int [k]; for (int i = 0 ; i < a.length; ++i){ c[a[i]-min] += 1 ; } for (int i = 1 ; i < c.length; ++i){ c[i] = c[i] + c[i-1 ]; } for (int i = a.length-1 ; i >= 0 ; --i){ b[--c[a[i]-min]] = a[i]; } return b; } }
Java版本的代码是我在维基上找的。它和前两个脚本语言的实现版本不同之处在于,javascript和ruby支持动态生成数组,无需给定数组长度,但是Java不行。可以看到Java的实现算法更复杂和抽象一点。
时间复杂度 当输入的元素是n
个0
到k
之间的整数,那么时间复杂度就是O(n+k)
,k
是数组中的最大值。这个时间复杂度可以从javascript的版本中得到体现。最优、最差以及平均时间复杂度都是O(n+k)
。
空间复杂度 空间复杂度和时间复杂度一样也是O(n+k)
,参照javascript版本中的实现,对数组长度的内存开销。
总结 因为要去理解Java版本的实现算法,以及一些其它的事情,导致这篇文章的发布进度落后了,很高兴没有停止我的挑战计划。说回算法,计数排序法是一种不太常用,但是稳定的排序算法。维基百科对它的评价是,计数排序不是比较排序,排序的速度快于任何比较排序算法。由于因为对内存空间的浪费,尤其是数组中有极大值的元素,这种情况下就不考虑技术排序了。