《算法导论》总结(一):线性时间排序算法

来源:互联网 发布:artcam二维编程 编辑:程序博客网 时间:2024/05/22 00:38

《算法导论》第一部分,第8章。这章介绍了3个线性时间排序的算法,也就是在O(n)的时间内排序的算法,O(n)时间复杂度是排序算法时间的极限,简单想一下也能明白,再好的算法也总得将所有数据全部读一遍才能知道顺序,所以复杂度至少也是n。虽然这几个算法排序的时间复杂度很低,但是这几个算法都有一定的应用限制,不适用于所以情形。不过,如果发现当前的应用环境符合这几个算法的限制,那用这几个算法能得到非常好的效果。

1、计数排序 (Counting Sort)

计数排序的限制条件是:所排对象的各个元素都是介于0--k的,并且这个k不是无限的,是在有限的内存里能用数组表示的。

 

计数排序的基本思想是,当读到一个数时,如果能确定出比这个元素小的数的个数,那这个数应该排的位置自然就知道了。为了实现这个目标,计数排序引入了2个辅助数组,BC。假设原数组是A,长度为n,数组B的长度也是n;假设每个元素的范围是0--k,那数组C的长度就是k。例如,已知我有一组数 2 5 3 0 2 3 0 3,所有数的范围都在0---5之间,那我们的数组C声明就应该是int C[6];

然后具体做法就是,遍历一遍A,读入一个数,就在对应C的位置+1,例如,读入一个数2,就让 C[2]++,这样,读完一遍以后就知道各个数出现了多少次。然后对C进行整理,得出比这个数小的有多少个,整理办法如下:

//assume C[0..k]

fori = 1 .. k do:

    C[i] = C[i-1] + C[i]

 

最后得出了各个数都有多少个比它小的个数以后,就可以进行最后的排序了:

遍历A,读入一个数x后就去C里找他应该放的位置,即C[x]的值就是他应该放的位置,放入B数组的该位置中,然后对应的C[x]--(因为考虑到可能有重复的数)

fori = 0 .. n do:

       x = A[i]

       index = C[x]

       B[index] = x

       C[x]--

 

2、基数排序 (Radix Sort)

基数排序算法的基本思想是,在待排序列里,分别从低位到高位依次排序,这样最后整个序列就有序了。例如,序列329,57,657,839,436 排序过程是这样的:

3 2 9

0 5 7

6 5 7

8 3 9

4 3 6

     ^

=>

4 3 6

0 5 7

6 5 7

3 2 9

8 3 9

  ^

=>

2 9

3 6

3 9

5 7

5 7

^

=>

0 5 7

3 2 9

4 3 6

6 5 7

8 3 9

这样看似更麻烦了,本来只需要排一次的东西现在要排好几次了,其实,这个主要是为了减轻上面1所讲的技术排序的限制,例如,如果一个序列里的数的范围很大,是2^32或者2^64,那么这个序列就无法用技术排序了,但是,如果用基数排序,每一轮用一次计数排序,总得时间复杂度是m*O(n)=O(n)。我们还可以根据内存等条件的具体情况,可以每2位或者3位作为一个单位进行排序。

这个算法要注意的一点是:排序时必须从后往前排,并且各个数字必须对齐。

 

3、桶排序 (Bucket Sort)

桶排序的算法假设前提是:假设要排的序列都是均匀分布在 [0, 1) 区间上的实数。这样就可以将 [0, 1) 区间平均分成m分,这样所有数都是近似平均的分到各个小区间里的,然后再对各个小区间进行排序,最后把整个桶连起来。再对小区间里的数进行排序时可以选用一些常规的排序算法(比如直接选择排序),因为我们的假设里有各个小区间里的数应该不会太多。整个算法的时间复杂度也是O(n),时间复杂度的具体推导过程书上写得很清楚。

 

 

 


一道习题:

假设有一张高考学生成绩表,表项是这样的:姓名、身份证号、成绩,一共有1000万高考考生,他们的成绩是0—900之间,可以有0.5,例如可能成绩是650.5,但不可能是650.3 。应该如何排序?

 

 

原创粉丝点击