希尔排序(Shell Sort)
- 是一种分组插入排序算法:
- 首先取一个整数d1=n/2,将元素分为d1个组,每组相邻两个元素之间距离为d1,在各组内进行直接插入排序
- 取第二个整数d2=d1/2,重复上述分组排序过程,直到di=1,即所有元素在同一组内进行直接插入排序
- 希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趟排序使得所有数据有序
- 排序速度:堆排序<希尔排序<快速排序
- 时间复杂度比较复杂,和选取的gap序列有关
代码:
def insert_sort(li): #原始插入排序
for i in range(1, len(li)): #表示摸到的牌的下标
temp = li[i] #摸到的牌的值
j = i - 1 #手里的牌的下标
while j >= 0 and li[j] > temp:
li[j+1] = li[j]
j -= 1
li[j+1] = temp
def insert_sort_gap(li, gap): #有gap的插入排序
for i in range(gap, len(li)): #表示摸到的牌的下标
temp = li[i] #摸到的牌的值
j = i - gap #手里的牌的下标
while j >= 0 and li[j] > temp:
li[j+gap] = li[j]
j -= gap
li[j+gap] = temp
def shell_sort(li): #希尔排序
d = len(li) // 2
while d >= 1:
insert_sort_gap(li, d)
d //= 2
计数排序(Count Sort)
- 统计所有元素,再判断每个元素有几个
- 非比较排序,时间复杂度为O(n),比较排序的时间复杂度最小为O(nlogn)
- 计数排序有限制:需要知道数的范围;需要消耗内存空间;无法小数...
代码:
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)
桶排序(Bucket Sort)
- 计数排序中元素范围比较大的改进算法
- 将元素分在不同的桶中,对每个桶中的元素排序
- 桶排序的表现取决于数据的分布,也就是需要对不同数据排序时采取不同的分桶策略
- 平均情况时间复杂度O(n+k),最坏情况O(kn^2),k=n(logn-logm),m为桶的个数
- 空间复杂度:O(kn)
代码:
def bucket_sort(li, n=100, max_num=10000): #n为桶的个数
buckets = [[] for _ in range(n)] #创建桶
for var in li:
i = min(var // (max_num // n), n-1) #i表示var放到哪个桶里
#如数为10000时,没有编号为100的桶,越界了,所以-1放在99号桶里
bucket[i].append(var) #把var加到桶里
#保持桶内的顺序
for j in range(len(bucket[i]-1, 0, -1):
if bucket[i][j] < bucket[i][j-1]:
bucket[i][j], bucket[i][j-1] = bucket[i][j-1], bucket[i][j]
else:
break
sorted_li = []
for buc in buckets:
sorted_li.extend(buc)
return sorted_li
基数排序(Radix Sort)
- 多关键字排序:例如先按年龄排序,再按薪资排序
- 数字的话按位数分桶排序,有几位就循环几次
- 时间复杂度O(kn),k是位数
- 空间复杂度O(k+n)
代码:
def radix_sort(li):
max_num = max(li)
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=3 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