一些非比较排序:计数排序,基数排序,桶排序
前面所讲述的算法皆为比较排序算法,在最坏情况下的最优排序效率为O(nlogn),如何超越比较排序的效率?在用比较进行排序的前提下,这个算法效率是无法超越的。
4.1 基于排序的规则
一个简单例子
‘’’
每一个排序关键字是1或者是2
的数组如何排序
‘’’
#思想:统计1出现的次数和2出现的次数
#假定为1出现的次数为k,择2出现的次数为n-k
#将填充到前k个位置,2填充到n-k的位置
A=[1,2,1,1,1,2,2]
def simple_sort(A):
n=len(A)
k=0
for i in range(n):
#统计1的个数
if A[i]==1:
k+=1
for i in range(k):
#将前k个数设置为1
A[i]=1
for i in range(k,n):
#将剩余n-k个数设置为2
A[i]=2
simple_sort(A)
print(A)
‘’’
时间复杂度为O(n),该算法并不会对元素进行比较
‘’’
#4.2基于比较排序的下界
‘’’
可以证明,只要是比较排序,其最差情况下的效率下界是O(nlogn)
排序算法的通用下界为O(n),即至少需要对每个元素进行一次检查
‘’’
#4.3计数排序
‘’’
如果序列的排序关键字是(0~~m)范围内的正整数,可以使用计算排序
直接寻找关键应该放置的索引位置,而且无论有无卫星数据,
这种计数排序的时间复杂度都是O(n),
这种排序并不是原址排序
这种排序是稳定排序
‘’’
#计数程序:对数组中每个元素的个数进行统计
#(要求数组中的数据都是大于或等于0的正整数)
def count_key(A):
"""
m:A中元素的取值范围(0~m)
return:equal[0...m] 表示A中每个元素出现的次数
"""
m=max(A)
#求数组中出现的最大元素
n=len(A)
equal=[0]*(m+1)
#可能存在的元素为(m+1)
#统计每个元素出现次数的数组
for i in range(0,n):
key=A[i]
equal[key]+=1
return equal
A=[1,2,1,2,1,2]
B=count_key(A)
print(B)
#统计less[]数组,数组中第表示less[j]中存放了排序关键字小于j的元素个数
def count_key_less(equal):
"""
equal:统计每个元素出现次数的数组
m: m:A中元素的取值范围(0~m)
return:输出:一个数组less[0..m]
表示less[j]中存放了排序关键字小于j的元素个数:
"""
n=len(equal)
less=[0]*n
#less[0]=0
for i in range(1,n):
less[i]=less[i-1]+equal[i-1]
return less
a=[0,3,3]
b=count_key_less(a)
print(b)
#计数排序函数
def count_sort(A,less):
"""
return 数组B,已经排好序
"""
n=len(A)
B=[0]*n
next=[0]*n
next=less
#不改变less数组
for i in range(0,n):
key=A[i]
index=next[key]
B[index]=key
next[key]=next[key]+1
return B
A=[1,2,1,2,1,2]
B=count_key(A)
less=count_key_less(B)
D=count_sort(A,less)
print(D)
#基数排序
‘’’
基数排序(Radix Sort)是一种非比较型整数排序算法
基本思想是:
将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。
按照低位先排序,分别放入10个队列中,然后采用先进先出的原则进行收集;
再按照高位排序,然后再收集;
依次类推,直到最高位,
最终得到排好序的数列。
对于数值偏小的一组序列,其速度是非常快的,
时间复杂度达到了线性,而且思想也非常的巧妙。
‘’’
注意:一定要按照从低位到高位的顺序排序。
‘’’
伪代码:
1.取得数组中的最大数,并取得位数;
2.对数位较短的数前面补零;
3.分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中;
4.收集,再将放置在0~9号桶中的数据按顺序放到数组中;
5.重复3~4过程,直到最高位,即可完成排序。
‘’’
from typing import List
def radix_sort(arr:List[int]):
n=len(str(max(arr)))
#求最高位数字的位数
for k in range(n):
#从低到高n轮排序
bucket_list=[[] for _ in range(10)]
#每一轮生成10个列表(因为数字为0~9,建立10个桶)
for i in arr:
#遍历数组元素
bucket_list[i//(10**k)%10].append(i)
#按照第k位放在桶中
arr=[j for i in bucket_list for j in i]
#按照当前桶的顺序重新排列数组
return arr
if __name__=="__main__":
import random
random.seed(54)
arr=[random.randint(0,100) for _ in range(10)]
print(arr)
arr_new=radix_sort(arr)
print(arr_new)
待完善:
???
1.桶排序和计数排序是一回事吗?
2.基数排序到底是基于桶排序还是基于计数排序的?
3.什么叫排序算法的稳定性?
桶排序