计数排序
计数排序的实现主要是数据样本的特殊性(正整数,明确最大边界)和利用列表索引位置来记录值,索引值来统计个数
最后循环索引,根据值(个数)确定添加多少个
import time
def cal_time(func):
def wrapper(*args, **kwargs):
t1 = time.time()
result = func(*args, **kwargs)
t2 = time.time()
print("%s running time: %s secs." % (func.__name__, t2 - t1))
return result
return wrapper
@cal_time
def count_sort(li, max_count=100):
count = [0 for _ in range(max_count+1)]
for val in li:
count[val] += 1
li.clear()
for ind, val in enumerate(count):
for i in range(val):
li.append(ind)
@cal_time
def sys_sort(li):
li.sort()
import random, copy
li = [random.randint(0,100) for _ in range(100000)]
li1 = copy.deepcopy(li)
li2 = copy.deepcopy(li)
count_sort(li1)
sys_sort(li2)
桶排序
桶排序实现思路和计数排序实现思路大体相同,计数排序是通过列表索引来把相同的数弄到一组,而桶排序则是确定一个范围来确定分组
代码实现思路:
- 根据桶数和最大数确定放入哪个桶
- 桶内进行冒泡排序
import random
def bucket_sort(li, n=100, max_num=10000):
buckets = [[] for _ in range(n)] # 创建桶
for var in li:
i = min(var // (max_num // n), n-1) # i 表示var放到几号桶里
buckets[i].append(var) # 把var加到桶里边
# 保持桶内的顺序
for j in range(len(buckets[i])-1, 0, -1):
if buckets[i][j] < buckets[i][j-1]:
buckets[i][j], buckets[i][j-1] = buckets[i][j-1], buckets[i][j]
else:
break
sorted_li = []
for buc in buckets:
sorted_li.extend(buc)
return sorted_li
li = [random.randint(0,10000) for i in range(100000)]
# print(li)
li = bucket_sort(li)
print(li)
桶排序不是一种特别理想的排序,如果数据分布不均匀,过多的数落入到一两桶里,排序效果就跟冒泡排序没有多大差别
基数排序
基数排序实现思路是多关键词排序和桶排序的结合,多关键词排序,举个例子,你就明白了,比如先工资排序,如果相同的再按年龄排序
基数排序实现过程:
- 先对个位进行排序,个数为相同的放同一个桶,合并桶
- 在对合并桶数据的十位排序,十位相同放一个桶中,合并桶
- 循环上述过程,个十百千万...
代码实现要点:获取最大数确定循环位数,每次循环桶数确定0-9,分桶合并桶
def radix_sort(li):
max_num = max(li) # 最大值 9->1, 99->2, 888->3, 10000->5
it = 0
while 10 ** it <= max_num:
buckets = [[] for _ in range(10)]
for var in li:
# 987 it=1 987//10->98 98%10->8; it=2 987//100->9 9%10=9
digit = (var // 10 ** it) % 10
buckets[digit].append(var)
# 分桶完成
li.clear()
for buc in buckets:
li.extend(buc)
# 把数重新写回li
it += 1
基数排序在某些情况下,比快速排序还快,是一种比较理想的排序,算法复杂度接近O(kn),它运算快慢更多取决于列表中数字大小,如果很大,它就会慢下来,而快排则是取决于列表长度,所以要根据不同场景来使用这个两个算法