vlambda博客
学习文章列表

第十六章 Caché 算法与数据结构 计数排序

第十六章 Caché 算法与数据结构 计数排序

定义

  • 假设20个随机整数的值如下所示。9,3,5,4,9,1,2,7,8,1,3,6,5,3,4,0,10,9 ,7,9。

  • 在0到10之间取值。数组定义大小为取值的最大范围

  • 下面就开始遍历这个无序的随机数列,每一个整数按照其值对号入座,同时, 对应数组下标的元素进行加1操作。

  • 例如第1个整数是9,那么数组下标为9的元素加1。

  • 第2个整数是3,那么数组下标为3的元素加1。

第十六章 Caché 算法与数据结构 计数排序

  • 继续遍历数列并修改数组……,最终,当数列遍历完毕时,数组的状态如下。

  • 该数组中每一个下标位置的值代表数列中对应整数出现的次数。

  • 有了这个统计结果,排序就很简单了。直接遍历数组,输出数组元素的下标值,元素的值是几,就输出几次。0,1,1,2,3,3,3,4,4,5,5,6,7,7,8,9,9,9,9,10 显然,现在输出的数列已经是有序的了。

使用场景

  • 它适用于一定范围内的整数排序。在取值范围不是很大的情况下,它的性能甚至快过那些时间复杂度为O(nlogn)的排序。

  • 当数列最大和最小值差距过大时,并不适合用计数排序。

  • 当数列元素不是整数时,也不适合用计数排序。

简单示例

计数类

 
   
   
 
  1. Class PHA.YX.Arithmetic.CountSort Extends %RegisteredObject

  2. {


  3. Method sort(array As PHA.YX.Arithmetic.Array)

  4. {

  5. /* 1.得到数列的最大值 */

  6. #dim max as %Integer = array.get(0)

  7. for i = 1 : 1 : (array.length() - 1) {

  8. if (array.get(i) > max){

  9. s max = array.get(i)

  10. }

  11. }


  12. /* 2.根据数列最大值确定统计数组的长度 */

  13. #dim countArray as PHA.YX.Arithmetic.Array = ##class(PHA.YX.Arithmetic.Array).%New()

  14. d countArray.init(max + 1)


  15. /* 3.遍历数列,填充统计数组 */

  16. for i = 0 : 1 : (array.length() - 1) {

  17. if (countArray.get(array.get(i)) = ""){

  18. d countArray.set(array.get(i), 0)

  19. }else{

  20. d countArray.set(array.get(i), (+countArray.get(array.get(i)) + 1))

  21. }


  22. }

  23. b ;countArray

  24. /* 4.遍历统计数组,输出结果 */

  25. #dim index as %Integer = 0

  26. #dim sortedArray as PHA.YX.Arithmetic.Array = ##class(PHA.YX.Arithmetic.Array).%New()

  27. d sortedArray.init(array.length())


  28. for i = 0 : 1 : (countArray.length() - 1) {

  29. for j = 0 : 1 : (countArray.get(i) ){

  30. d sortedArray.set(index, i)

  31. s index = index + 1

  32. }

  33. }

  34. b ;sortedArray

  35. q sortedArray

  36. }

  37. }

调用

 
   
   
 
  1. /// w ##class(PHA.YX.Arithmetic).CountSort()

  2. ClassMethod CountSort()

  3. {

  4. #dim array as PHA.YX.Arithmetic.Array = ##class(PHA.YX.Arithmetic.Array).%New()

  5. d array.init(13)

  6. d array.insert(0,4)

  7. d array.insert(1,4)

  8. d array.insert(2,6)

  9. d array.insert(3,5)

  10. d array.insert(4,3)

  11. d array.insert(5,2)

  12. d array.insert(6,8)

  13. d array.insert(7,1)

  14. d array.insert(8,7)

  15. d array.insert(9,5)

  16. d array.insert(10,6)

  17. d array.insert(11,0)

  18. d array.insert(12,10)


  19. #dim countSort as PHA.YX.Arithmetic.CountSort = ##class(PHA.YX.Arithmetic.CountSort).%New()

  20. s array = countSort.sort(array)


  21. d array.output()


  22. q ""

  23. }

 
   
   
 
  1. <BREAK>zsort+30^PHA.YX.Arithmetic.CountSort.1

  2. DHC-APP 3e1>g

  3. 0

  4. 1

  5. 2

  6. 3

  7. 4

  8. 4

  9. 5

  10. 5

  11. 6

  12. 6

  13. 7

  14. 8

  15. 9

  16. 10

优化

问题

  • 我们只以数列的最大值来决定统计数组的长度,其实并不严谨。例如下面的数列。95,94,91,98,99,90,99,93,91,92。

  • 这个数列的最大值是99,但最小的整数是90。如果创建长度为100的数组,那么前面从0到89的空间位置就都浪费。

解决

  • 以最大值减去最小值的差作为数组的长度。

  • 以刚才的数列为例,统计出数组的长度为99-90+1=10,偏移量等于数列的最小值90。

优化版本

计数类

 
   
   
 
  1. Method optimizeSort(array As PHA.YX.Arithmetic.Array)

  2. {

  3. /* 1.得到数列的最大值和最小值,并算出差值d */

  4. #dim max as %Integer = array.get(0)

  5. #dim min as %Integer = array.get(0)

  6. for i = 1 : 1 : (array.length() - 1) {

  7. if (array.get(i) > max){

  8. s max = array.get(i)

  9. }

  10. if (array.get(i) < min){

  11. s min = array.get(i)

  12. }

  13. }

  14. #dim d as %Integer = max - min


  15. /* 2.创建统计数组并统计对应元素个数 */

  16. #dim countArray as PHA.YX.Arithmetic.Array = ##class(PHA.YX.Arithmetic.Array).%New()

  17. d countArray.init(d + 1)


  18. for i = 0 : 1 : (array.length() - 1) {

  19. if (countArray.get((array.get(i)- min)) = ""){

  20. d countArray.set((array.get(i) - min), 1)

  21. }else{

  22. d countArray.set((array.get(i) - min), (+countArray.get((array.get(i) - min)) + 1))

  23. }


  24. }


  25. b ;2 countArray

  26. /* 3.统计数组做变形,后面的元素等于前面的元素之和 */

  27. for i = 1 : 1 : countArray.length - 1 {

  28. d countArray.set(i , (countArray.get(i) + countArray.get(i - 1)))

  29. }


  30. b ;3 countArray

  31. /* 4.倒序遍历原始数列,从统计数组找到正确位置,输出到结果数组 */

  32. #dim sortedArray as PHA.YX.Arithmetic.Array = ##class(PHA.YX.Arithmetic.Array).%New()

  33. d sortedArray.init(array.length())


  34. for i = (countArray.length() - 1) : -1 : 0 {

  35. s arrayMin = array.get(i) - min

  36. s countMin = countArray.get(arrayMin) - 1

  37. d sortedArray.set(countMin, array.get(i))

  38. d countArray.set(arrayMin, countMin)

  39. }

  40. b ;sortedArray

  41. q sortedArray

  42. }

调用

 
   
   
 
  1. /// w ##class(PHA.YX.Arithmetic).OptimizeCountSort()

  2. ClassMethod OptimizeCountSort()

  3. {

  4. #dim array as PHA.YX.Arithmetic.Array = ##class(PHA.YX.Arithmetic.Array).%New()

  5. d array.init(10)

  6. d array.insert(0,95)

  7. d array.insert(1,94)

  8. d array.insert(2,91)

  9. d array.insert(3,98)

  10. d array.insert(4,99)

  11. d array.insert(5,90)

  12. d array.insert(6,99)

  13. d array.insert(7,93)

  14. d array.insert(8,91)

  15. d array.insert(9,92)


  16. #dim countSort as PHA.YX.Arithmetic.CountSort = ##class(PHA.YX.Arithmetic.CountSort).%New()

  17. s array = countSort.optimizeSort(array)


  18. d array.output()


  19. q ""

  20. }

 
   
   
 
  1. DHC-APP>w ##class(PHA.YX.Arithmetic).OptimizeCountSort()

  2. 90

  3. 91

  4. 91

  5. 92

  6. 93

  7. 94

  8. 95

  9. 98

  10. 99

  11. 99

时间复杂度

  • 代码第1、2、4步都涉及遍历原始数列,运算量都是n,第3步遍历统计数列,运算量是m,所以总体运算量是3n+m,去掉系数,时间复杂度是O(n+m)。

空间复杂度

  • 至于空间复杂度,如果不考虑结果数组,只考虑统计数组大小的话,空间复杂度是O(m)。