本章围绕四小节展开,第一小节证明任何比较排序在最坏情况下都要经过Ω(nlgn) 次比较,第二、三、四小节分别讨论计数排序、基数排序、桶排序三种线性时间复杂度的排序算法。
排序算法的下界
比较排序算法具备的性质:在排序的最终结果中,各元素的次序依赖于它们之间的比较。
比较排序可以被抽象为一棵决策树,决策树是一棵完全二叉树,它可以表示在给定输入规模情况下,某一特定排序算法对所有元素的比较操作。
定理:在最坏情况下,任何比较排序算法都需要做Ω(nlgn) 次比较。
推论:堆排序和归并排序都是渐进最优的比较排序算法。
计数排序
计数排序:假设n个输入元素中的每一个都是在0到k区间的一个整数,其中k为某个整数。当k=O(n)时,排序运行时间为θ(n)。计数排序是一种稳定的排序算法。
算法思想:对每一个输入元素x,确定小于x的元素个数,利用该信息就可以直接把x放到它在输出数组中的位置。
计数排序代码实现如下:
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
class CountingSort:
def sort(self, A, k):
C = [0] * (k + 1)
for j in range(len(A)):
C[A[j]] = C[A[j]] + 1
for i in range(1, k + 1):
C[i] = C[i] + C[i - 1]
B = [0] * len(A)
for j in range(len(A) - 1, -1, -1):
B[C[A[j]] - 1] = A[j]
C[A[j]] -= 1
for i in range(len(A)):
A[i] = B[i]
def test():
cs = CountingSort()
A = [2, 5, 3, 0, 2, 3, 0, 3]
cs.sort(A, 5)
print(A)
if __name__ == '__main__':
test()
基数排序
基数排序:按最低有效位进行排序,是一种稳定的排序算法,下图为一组数据的基数排序过程。
引理:给定n个d位数,其中每一个数位有k个可能的取值,如果基数排序使用的稳定排序方法耗时θ(n+k),那么它就可以在θ(d(n+k))时间内讲这些数排好序。
引理:给定n个b位数和任何正整数r≤b,如果基数排序使用的稳定排序算法对数据取值区间是0到k的输入进行排序耗时θ(n+k),那么它就可以在θ((b/r)(n+2^r))时间内讲这些数排好序。
基数排序代码实现如下:
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
class RadixSort:
def sort(self, A, d):
temp = A.copy()
for i in range(d):
temp = [t // 10 for t in temp]
self.__count_sort(A, 9, temp)
def __count_sort(self, A, k, B):
keys = [num % 10 for num in B]
C = [0] * (k + 1)
for j in range(len(keys)):
C[keys[j]] = C[keys[j]] + 1
for i in range(1, k + 1):
C[i] = C[i] + C[i - 1]
tempA = [0] * len(A)
tempB = [0] * len(B)
for j in range(len(A) - 1, -1, -1):
tempA[C[keys[j]] - 1] = A[j]
tempB[C[keys[j]] - 1] = B[j]
C[keys[j]] -= 1
for i in range(len(A)):
A[i] = tempA[i]
B[i] = tempB[i]
def test():
rs = RadixSort()
A = [329, 457, 657, 839, 436, 720, 355]
rs.sort(A, 3)
print(A)
if __name__ == '__main__':
test()
桶排序
桶排序:假设输入是由一个随机过程产生,该过程将元素均匀、独立地分布在[0,1)区间上,桶排序将区间划分为n个相同大小的子区间,即桶,之后将n个输入数分别放到各个桶中。若假设输入输入数据服从均匀分布,平均情况下它的时间代价为O(n) 。桶排序是稳定的排序算法。
桶排序代码实现如下:
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
class BucketSort:
def sort(self, A):
import math
n = len(A)
B = []
for i in range(n):
B.append([])
for i in range(n):
B[math.floor(n * A[i])].append(A[i])
for i in range(n):
B[i].sort()
i = 0
for j in range(n):
for num in B[j]:
A[i], i = num, i + 1
def test():
bs = BucketSort()
A = [0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12, 0.23, 0.68]
bs.sort(A)
print(A)
if __name__ == '__main__':
test()