算法导论 — 8.2 计数排序

笔记

假设输入的nn个元素中的每一个都是在00 ~ kk内的一个整数。kk表示了输入元素所处的范围。一般在kk远小于nn时,可以采用计数排序算法。
  计数排序的基本思想是:对每一个输入元素xx,确定小于xx的元素个数。利用这一信息,可以直接把xx放到正确的位置。例如,如果有1717个元素小于xx,则x就应该放在第1818个位置上。
  以下为计数排序的伪代码。假设输入数组为A[1..n]A[1..n],输出数组为B[1..n]B[1..n],另外还有一个数组C[0..k]C[0..k]提供临时存储空间。
  算法导论 — 8.2 计数排序
  下图给出了一个例子,输入数组为<2,5,3,0,2,3,0,3><2, 5, 3, 0, 2, 3, 0, 3>
  算法导论 — 8.2 计数排序
  在计数排序的伪代码中,第22 ~ 33行的for循环所花时间为Θ(k)Θ(k),第44 ~ 55行的for循环所花时间为Θ(n)Θ(n),第77 ~ 88行的for循环所花时间为Θ(k)Θ(k),第1010 ~ 1212行的for循环所花时间为Θ(n)Θ(n)。这样,计数排序总的时间代价为Θ(k+n)Θ(k+n)。在实际应用中,一般在k=O(n)k = O(n)时才会应用计数排序,此时计数排序的运行时间为Θ(n)Θ(n)
  在8.1节,我们得到结论,比较排序的运行时间的下界为Ω(nlgn)Ω(n{\rm lg}n)。然而计数排序的运行时间是优于Ω(nlgn)Ω(n{\rm lg}n)的,因为它并不是一个比较排序算法。
  计数排序还是一个稳定排序算法,这一玄机在于伪代码的第1010行,从后向前遍历输入数组A[1..n]A[1..n]。假如在A[1..n]A[1..n]存在一组数值相同的元素,位置靠后的元素先取出,它在输出数组中的位置也相对靠后;而位置靠前的元素后取出,它在输出数组中的位置也相对靠前。

练习

8.2-1 参照图8-2的方法,说明COUNTINGSORTCOUNTING-SORT在数组A=<6,0,2,0,1,3,4,6,1,3,2>A = <6, 0, 2, 0, 1, 3, 4, 6, 1, 3, 2>上的操作过程。
  
  算法导论 — 8.2 计数排序
  
8.2-2 试证明COUNTINGSORTCOUNTING-SORT是稳定的。
  

8.2-3 假设我们在COUNTINGSORTCOUNTING-SORT的第10行循环的开始部分,将代码改写为:
  算法导论 — 8.2 计数排序
  试证明该算法仍然是正确的。它还稳定吗?
  

8.2-4 设计一个算法,它能够对于任何给定的介于00kk之间的nn个整数先进行预处理,然后在O(1)O(1)时间内回答输入的nn个整数中有多少个落在区间[a..b][a..b]内。你设计的算法的预处理时间应为Θ(n+k)Θ(n+k)
  
  以下直接给出伪代码。
  算法导论 — 8.2 计数排序

本节相关代码链接:
  https://github.com/yangtzhou2012/Introduction_to_Algorithms_3rd/tree/master/Chapter08/Section_8.2