所有我们之前讨论的排序算法都是基于比较,即通过比较数的大小来对序列中的元素进行排序,比较排序的最好的最坏排序时间为$O(n \lg n)$。今天介绍的三种算法将突破这一限制,以线性时间运行。
# 一、排序算法下界
**定理:** 任何比较排序算法,在最坏情况下都要与$\Omega(n \lg n)$作比较;
证明略;
# 二、计数排序
计数排序的基本思想就是确定比每个输入元素小的元素个数,以此直接将每个元素放到最终输出数组的位置上;
```
//伪代码
INSERTION-SORT(A)
for j←2 to length(A)
do key ← A[j]∥insert A[j] into the sorted sequence A[l…j-1]
i ← j-1
while i>0 and A[i]>key
do A[i+1] <A[i] //move item back
A[i+l] ← key //find the insertion position
```
当输入的元素是0到k之间的整数时,时间复杂度是O(n+k),空间复杂度也是O(n+k)。当k不是很大并且序列比较集中时,计数排序是一个很有效的排序算法。计数排序是一个稳定的排序算法。
# 三、基数排序
基数排序(Radix Sort)是一种非比较型排序算法,它将整数按位数切割成不同的数字,然后按每个位分别进行排序。
LSD(从最低有效位开始排序)基数排序实现的基本思路是将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。需要注意的是,对每一个数位进行排序的算法必须是稳定的,否则就会取消前一次排序的结果。通常我们使用计数排序或者桶排序作为基数排序的辅助算法。
```
//伪代码
RADIX-SORT(A, d)
for i ← 1 to d
do use a stable sort to sort array A on digit i
```
基数排序的时间复杂度是$O(k\cdot n)$,其中n是排序元素个数,k是数字位数。注意这不是说这个时间复杂度一定优于$O(n \lg n)$,因为n可能具有比较大的系数k。
# 四、桶排序
桶排序(Bucket Sort)的思想是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法)。当要被排序的数组内的数值是均匀分配的时候,桶排序可以以线性时间运行。
![](https://static.waverly.top/img/20200412084430.png#vwid=794&vhei=442)
```
//伪代码
BUCKET-SORT(A)
n ← length [A]
for i ← 1 to n
do insert A[il into list B[nA[i](向上取整)]
for i←0 to n-1
do sort list b[i] with insertion sort
concatenate the list b[O],b[l].B[n-1] together in order
```
要分析排序时间,让$n_{i}$成为一个随机变量,表示放置在存储桶B[i]中的元素数。排序的总时间是:
$$T(n)=\Theta(n)+\sum_{i=0}^{n-1} O\left(n_{i}^{2}\right)$$
12-12