CLRS笔记8,线性时间排序(counting、radix、bucket sort)

线性时间排序即时间复杂度为Θ(n)的排序,主要有计数排序、基数排序和桶排序
以前的排序都涉及到元素比较,称为比较排序,渐近最优的为merge sort和heap sort,时间复杂度为Θ(nlogn)
而这种排序都不是用比较的操作来排序,所以下届Θ(nlogn)对它们不适用

[b]计数排序[/b]
1,假设数组a所有元素i满足1<=i<=k,建立数组b且初始值为0
2,读取数列每个元素,如果元素为i,则b[i]的值加一
3,遍历数组b,如果b[i]=j,就输出j次i,这样输出的序列就是已经排好序的
时间复杂度为Θ(n+k),k为常数
计数排序适合所需排序的数组元素取值范围不大的情况,如果每个元素都不一样,则效率不高

def counting_sort(a)
min = a.min
max = a.max
counts = Array.new(max-min+1, 0)

a.each do |n|
counts[n-min] += 1
end

(0...counts.size).map{|i| [i+min]*counts[i]}.flatten
end


[b]基数排序[/b]
1,假设数组a所有元素i为十进制整数且位数不超过d位
2,递归对数组a的个位、十位、...、d位排序
当没位数字都界于1到k之间时,对n个d位数的每一位处理的时间为Θ(n+k),共d次处理,所以时间复杂度为Θ(dn+dk),d为常数
基数排序分LSD(Least significant digital)和MSD(Most significant digital),前者从最低位开始排,后者从最高位开始排
LSD需要保持较低位的位置,而MSD则不需要
LSD排序图示:
数列内容:421 240 35 532 305 430 124
第1次分组
组0 240 430
组1 421
组2 532
组3
组4 124
组5 35 305
组6
组7
组8
组9
数列内容:240 430 421 532 124 35 305
第2次分组
组0 305
组1
组2 421 124
组3 430 532 35
组4 240
组5
组6
组7
组8
组9
数列内容:305 421 124 430 532 35 240
第3次分组
组0 35
组1 124
组2 240
组3 305
组4 421 430
组5 532
组6
组7
组8
组9
数列内容:35 124 240 305 421 430 532
LSD的Ruby代码实现:

def kth_digit(n, i) # 求数字number的第kth位数字
while(i > 1)
n = n / 10
i = i - 1
end
n % 10
end

def radix_sort(a)
max = a.max # 最大元素
d = Math.log10(max).floor + 1 # 最大元素的位数

(1..d).each do |i|
tmp = [] # 分配10个桶
(0..9).each do |j|
tmp[j] = []
end

a.each do |n|
kth = kth_digit(n, i)
tmp[kth] << n # 把元素放入桶中
end
a = tmp.flatten # 把桶中元素串起来并放回原数组
end
return a
end


[b]桶排序[/b]
1,假设数组a中的数字是平均分布的
2,把数组分组,放到一个个的桶中,然后对每个非空的桶排序
假设有n个数字,m个桶,平均每个桶有n/m个数字,整个算法的时间复杂度为Θ(n + m*(n/m)*log(n/m))=Θ(n+n*log(n/m)),当m接近n时复杂度接近Θ(n)
如果所有的数字都落在同一个桶中,就退化成比较排序了

def quick_sort(a) # 快速排序
(x=a.pop) ? quick_sort(a.select{|i| i <= x}) + [x] + quick_sort(a.select{|i| i > x}) : []
end

def first_number(n) # 小数点后第1个数
(n * 10).to_i
end

def bucket_sort(a)
tmp = [] # 分配10个桶
(0..9).each do |i|
tmp[i] = []
end

a.each do |n|
k = first_number(n)
tmp[k] << n # 把元素放入桶中
end

(0..9).each do |j|
tmp[j] = quick_sort(tmp[j]) # 对每个桶快速排序
end

tmp.flatten
end

a = [0.75, 0.13, 0, 0.44, 0.55, 0.01, 0.98, 0.1234567]
p bucket_sort(a)

# Result:
[0, 0.01, 0.1234567, 0.13, 0.44, 0.55, 0.75, 0.98]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值