1 本节思路
之前的算法的最基本的思想是比较元素大小,所以算法复杂度最好是Θ(nlogn)\Theta(nlogn)Θ(nlogn),本节不再基于元素比较,而是基于计数的Counting sort,然后应用在Radix sort上。
2 Counting sort
2.1 算法思想
Counting sort算法的思想比较简单,就是通过统计每个数字的的个数确定每个数字的位置,譬如【8,6,2】这三个数,通过计数统计知道#8=1,#6=1,#2=1,很自然就是2排正位置1上,6排在位置1+1=2上,8排在位置2+1=3上,因为每个数字都是不重复,所以看起来很low,考虑到有充分数字的情况,就会领会到这个算法的巧妙之处了。
2.2 算法演示
2.3 算法描述
2.4 算法实现
# -*- coding: utf-8 -*-
import collectionsdef counting_sort(A, B, k):# let C[0..k] be a new arrayC = [0] * (k + 1)for i in range(k + 1):C[i] = 0for j in range(1, len(A)):C[A[j]] = C[A[j]] + 1# C[i] now contains the number of elements equal to i.for i in range(1, k + 1):C[i] = C[i] + C[i - 1]# C[i] now contains the number of elements less than or equal to i.for j in range(len(A) - 1, 0, -1):B[C[A[j]]] = A[j]C[A[j]] = C[A[j]] - 1if __name__ == '__main__':A = ['x', 2, 5, 3, 0, 2, 3, 0, 3]B = [0] * len(A)k = max(A[1:])print('before sort:', A[1:])counting_sort(A, B, k)print('after sort:', B[1:])
运行结果:
before sort: [2, 5, 3, 0, 2, 3, 0, 3]
after sort: [0, 0, 2, 2, 3, 3, 3, 5]
2.5 算法分析
容易得到Counting sort的算法复杂度是O(k+n)O(k+n)O(k+n),k为集合中元素个数,n为排序序列中元素个数,因为k<nk<nk<n,所以可以认为这个算法是线性时间复杂度,其原因就是空间换时间,所以 其用途在于小范围内的数据,如果数据范围过大(排序的元素个数不一定很多)会占用大量的内存,下面Radix sort正好可以应用到这一点。
3 Radix sort
3.1 Radix sort算法思想
Radix sort按位从后至前排序,因为要防止重复数字问题,所以顺序是从后至前。下面的演示很简单的表达了这种思想:
3.2 算法描述
这里stable sort 指的是counting sort。
3.3 算法分析
- 考虑一个极端的情况,在一个b=32位的计算机上,每一个数字都可以表示为b个bit的序列,此时b=log2nb=log_2{n}b=log2n,应用上面的lemma8.3容易得到,在这种情况下使用RADIX-SORT的算法复杂度是Θ(b(n+2))=Θ(logn(n+2))\Theta(b(n+2))=\Theta(logn(n+2))Θ(b(n+2))=Θ(logn(n+2)),之前的基于“元素比较”排序算法没有什么提高。
- 考虑另一个方向的极端情况,假如计算机内存超级大随便用(不考虑缓存消耗),那么上面问题的复杂度是Θ(n+32)\Theta(n+32)Θ(n+32),理论上完美,实际上不可信。
- 两个极端的情况之间存在着一种权衡,这种权衡关系如下面lemma8.4所示,这个lemma8.4中使用适当的颗粒度r作为一位进行counting sort。
- 有点问题。。。待续。。。