线性时间排序

线性时间排序

Counting sort(计数排序)

计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为 O ( n + k ) Ο(n+k) O(n+k)(其中 k k k是整数的范围),快于任何比较排序算法。当然这是一种牺牲空间换取时间的做法,而且当 O ( k ) &gt; O ( n ⋅ l o g ( n ) ) O(k)&gt;O(n·log(n)) O(k)>O(nlog(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)1,故适用于排序规模相对较小时( k &lt; n ⋅ l g n k&lt;n·lgn k<nlgn)使用。

1.算法步骤

由于伪代码难以让人理解,我们以一个例子开始


1、我们有一个待排序数组 A A A,并在A下给予相应的编号(从1开始):
A = [ 4 1 , 1 2 , 3 3 , 4 4 , 3 5 ] A=[4_1,1_2,3_3,4_4,3_5] A=[41,12,33,44,35]

我们根据A的取值范围定义一个数组 C C C(长度为 m a x ( C ) max(C) max(C)- m i n ( C ) min(C) min(C)
其中下标为对应 A A A的值的位置
C = [ 0 1 , 0 2 , 0 3 , 0 4 ] C =[0_1,0_2,0_3,0_4] C=[01,02,03,04]
2、我们根据编号从一开始,将 A A A遍历一遍,同时在 C C C中将相应的值每次加一。
换一种思路: C C C对应的值为在 A A A中遍历到的位置的值 − m i n ( C ) -min(C) min(C) C [ A [ 当 前 位 置 ] − 1 ] C[A[当前位置]-1] C[A[]1]
则有:
第一个位置的数为 4 4 4,我们将 C C C中的 4 4 4位置对应的数 + 1 +1 +1,则变更以后的 C C C为:
C = [ 0 1 , 0 2 , 0 3 , 1 4 ] C =[0_1,0_2,0_3,1_4] C=[01,02,03,14]
同理,第二个数为 1 1 1,则我们将 2 2 2对应的数数 + 1 +1 +1,则变更以后的 C C C为:
C = [ 1 1 , 0 2 , 0 3 , 1 4 ] C =[1_1,0_2,0_3,1_4] C=[11,02,03,14]
最终我们得到的 C C C为:
C = [ 1 , 0 , 2 , 2 ] C =[1,0,2,2] C=[1,0,2,2]
3、我们对 C C C进行处理,将将 C C C从第一个位置开始,每一个位置的值为原来此位置的值加上前一个位置的值,我们将之叫为 C ′ C&#x27; C
即:
C [ 1 ] = C [ 1 ] + C [ 0 ] = 1 C[1] = C[1]+C[0] = 1 C[1]=C[1]+C[0]=1
C [ 2 ] = C [ 2 ] + C [ 1 ] = 3 C[2] = C[2]+C[1] = 3 C[2]=C[2]+C[1]=3
C [ 3 ] = C [ 3 ] + C [ 2 ] = 5 C[3] = C[3]+C[2] = 5 C[3]=C[3]+C[2]=5
C ′ = [ 1 , 1 , 3 , 5 ] C&#x27; = [1,1,3,5] C=[1,1,3,5] 为了节省空间我们可以直接在 C C C上操作,但为了易于辨别,我们将操作后的数组命名为 C ′ C&#x27; C
4、我们新建一个数组,这个数组的长度与带排序的数组相同:
A ′ = [ 0 , 0 , 0 , 0 , 0 ] A&#x27;=[0,0,0,0,0] A=[0,0,0,0,0]A'中的值可以为空,仅需要长度与A相同即可

我们从A开始进行一次遍历,将每一个数字直接放在其应该在的位置上。从 A A A中的第一个数字开始,值为 4 4 4,在 C ′ C&#x27; C中,我们找到第 4 4 4列,即 4 4 4所对应 C ′ C&#x27; C中的列(我们是按照待排数值的范围确定 C C C的长度的,若以下标来讲,我们 C ′ C&#x27; C所对应的值为 C ′ [ A [ 当 前 位 置 ] − m i n ( C ) ] C&#x27;[A[当前位置]-min(C)] C[A[]min(C)]).
我们取到
C ′ [ 4 ] = 5 C&#x27;[4] = 5 C[4]=5
再将 A ′ A&#x27; A中的第 5 5 5列的值赋值为 4 4 4(待排数的值),将 C ′ C&#x27; C 4 4 4列的值 − 1 -1 1
新的数组为:
C ′ = [ 1 , 1 , 3 , 4 ] C&#x27; = [1,1,3,4] C=[1,1,3,4]
A ′ = [ 0 , 0 , 0 , 0 , 4 ] A&#x27;=[0,0,0,0,4] A=[0,0,0,0,4]
同理, A A A中的第二个数为 1 1 1,在 C ′ C&#x27; C中第一列,值为 1 1 1,则我们将 A ′ A&#x27; A中的第 1 1 1个位置赋值为 1 1 1,将 C ′ C&#x27; C 1 1 1列的值 − 1 -1 1,则第 2 2 2个赋值之后新的数组为:
C ′ = [ 0 , 1 , 3 , 4 ] C&#x27; = [0,1,3,4] C=[0,1,3,4]
A ′ = [ 1 , 0 , 0 , 0 , 4 ] A&#x27;=[1,0,0,0,4] A=[1,0,0,0,4]
我们省去过程,将每一次的迭代结果给出:
第三次:
C ′ = [ 0 , 1 , 2 , 4 ] C&#x27; = [0,1,2,4] C=[0,1,2,4]
A ′ = [ 1 , 0 , 3 , 0 , 4 ] A&#x27;=[1,0,3,0,4] A=[1,0,3,0,4]
第四次:
C ′ = [ 0 , 1 , 2 , 3 ] C&#x27; = [0,1,2,3] C=[0,1,2,3]
A ′ = [ 1 , 0 , 3 , 4 , 4 ] A&#x27;=[1,0,3,4,4] A=[1,0,3,4,4]
第五次:
C ′ = [ 0 , 1 , 1 , 3 ] C&#x27; = [0,1,1,3] C=[0,1,1,3]
A ′ = [ 1 , 3 , 3 , 4 , 4 ] A&#x27;=[1,3,3,4,4] A=[1,3,3,4,4]
经过这一次的遍历,我们可直接得到 A ′ A&#x27; A这个排序完毕的数组。

多么美妙的排序过程啊!
然而太大的Cache占用过多的Cache访问会导致时间相较于Quicksort还要长,但在开头的分析之下,排序速度依然是Counting sort占优。所以更好的办法是我们将QuicksortCounting sort结合使用,相关的内容及结合后的时间复杂度分析详见另一篇Blog。

Radix sort (基数排序)

1、算法步骤

作为一个古老的排序算法(大约出现在1890),我们依然已一个例子来作演示。
1、设我们的待排数组为
A = [ 329 , 457 , 657 , 839 , 436 , 720 , 355 ] A=[329,457,657,839,436,720,355] A=[329,457,657,839,436,720,355]
由于算法是从最后一位开始排序的,我们将他们竖着写
329 329 329
457 457 457
657 657 657
839 839 839
436 436 436
720 720 720
355 355 355
我们从待排数组的最后一位开始,从 0 − 9 0-9 09即由小到大排列
2、由于第一小的是 0 0 0则我们将 720 720 720放在第一个,第二小的为 5 5 5我们将 355 355 355放在 720 720 720后面,值得注意的是我们需要考虑排序的稳定性,即需要自上而下开始查看。
第二位与第一位暂时不需要考虑,则我们得到的数列为
720 720 720
355 355 355
436 436 436
457 457 457
657 657 657
329 329 329
839 839 839
3、同理可得对于倒是第二位的排序:
720 720 720
329 329 329
436 436 436
839 839 839
355 355 355
457 457 457
657 657 657
4、再对最后一位进行排序:
329 329 329
355 355 355
436 436 436
457 457 457
657 657 657
720 720 720
839 839 839


  1. 比较排序的时间复杂度下限证明详见两一篇blog,地址:比较算法时间复杂度最小值的证明↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值