总结:这章主要讲了非比较排序的几种算法,计数排序、基数排序、桶排序。
1. 排序算法时间的下届
比较排序:排序结果中,各元素的次序基于输入元素间的比较。
比较排序可以被抽象为决策树
定理:任意一个比较排序算法在最坏情况下,都需要做Ω(nlgn)次的比较。
2. 计数排序
假设:n个输入元素中的每一个都是介于0到k之间的整数
基本思想:对每一个输入元素x,确定出小于x的元素个数
分析: O(n+k),当k=O(n)是,运行时间为O(n)
稳定排序
假定输入是个数组A[1...n],length[A]=n,存放排序结果的B[1..n],提供临时存储区的C[0..k]
伪代码
COUNTING-SORT(A, B, k)
for i <- 1 to k
do C[i] <- 0
for i <- 1 to length[A]
do C[A[i]] <- C[A[i]]+1 //C[i]包含等于i的元素个数
for i <- 1 to k
do C[i] <- C[i-1]+C[i] //C[i]包含了小于等于i的元素个数
for i <- length[A] downto 1 //从后往前,保证了排序的稳定性
do B[C[A[i]]] <- A[i]
C[A[i]] <- C[A[i]]-1
3. 基数排序
按最低有效位进行稳定排序(可以用计数排序)
输入数组A,A中每个元素都有d位数字
分析:O(d(n+k))
伪代码
RADIX-SORT(A, d)
for i <- 1 to d
do use a stable sort to sort array A on digit i
4. 桶排序
算法思想:把区间分成桶,将输入的元素放入对应桶中,对桶中的元素进行排序(可以是插入排序)
分析:O(n)
伪代码
BUCKET-SORT(A)
n <- length[A]
for i <- 1 to n
do insert A[i] into list B[(nA[i])]
for i <- 0 to n-1
do sort list B[i] with insertion sort
concatenate the lists B[0], B[1], …, B[n-1] together in order
【练习题】
1. 说明如何在O(n)时间内,对0到n^2-1之间的n个数进行排序。
说明:此时k=O(n^2)了,不能直接用计数排序。可以将数表示为2位n进制数,每位的范围0~n-1,使用基数排序,复杂度O(n+n)=O(n)
【思考题】
1. 排序不同长度的数据项
1) 给定一个整数数组,其中不同的整数中包含的数字个数可能不同,但该数组中,所有整数中总的数字数为n。说明如何在O(n)时间内对该数组进行排序。
思路:首先按数字的个数用计数排序,对数字个数相同的用基数排序。
2) 给定一个字符串数组,其中不同的串包含的字符数可能不同,但所有串中总的字符个数为n。说明如何在O(n)时间内对该数组进行排序。(eg. a < ab < b)
思路:首先按字符串的首字母排序,去掉首字母,再按首字母排序,这样递归。