一、希尔排序
1、方法
一种分组插入的排序算法
首先取一个整数d1=n/2,将元素分成d1个组,每组相邻两元素之间的距离为d1,在各组内进行直接插入排序;
取第二个数d2=d1/2,重复上述分组排序过程,直到di=1,即所有元素在同一组内进行直接插入排序。
注:希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趟排序使得所有数据有序。
例: 5 7 4 6 3 1 2 9 8
每间隔d = 4分为一组: 5 7 4 6 3 1 2 9 8
分成上述分组,颜色相同的分为一组,组内进行排序后为 3 1 2 6 5 7 4 9 8
再由d = 2进行分组 3 1 2 6 5 7 4 9 8 颜色相同的分为一组,进行组内直接插入排序。
最后相当于d = 1分组,进行直接分组插入排序,最终使得数组有序。
2、代码实现
# 希尔排序,代码实现
def insert_sort_gap(li, gap): # 实现分组插入排序
for i in range(gap, len(li)-1):
tmp = li[i] # 暂存无序区第一个数
j = i - gap # 每组内有序区最后一个数的下标
while j >= 0 and li[j] > tmp: # 如果有序区的数大于无序区的数
li[j+gap] = li[j] # 将有序区的数的位置往后移动一位
j -= gap
li[j+gap] = tmp # 将tmp插入到有序区合适的位置
def shell_sort(li):
d = len(li) // 2 # 先分为两组
while d >= 1:
insert_sort_gap(li, d)
d //= 2 # 每次分组的组内间距减少一半
# 希尔排序的时间复杂度与选取的gap序列有关
li = list(range(100))
import random
random.shuffle(li)
print(li)
shell_sort(li)
print(li)
二、计数排序
1、方法
已知列表中有多少个数n,构造一个新的长度为n的列表,遍历原列表,如果第 i 个数为 j ,则在新列表对应大小的下标处进行加1,遍历完成后清除列表。
将新列表中的元素“取出”,新列表中的下标对应原列表中的数的大小,新列表中的下标的数为原列表中的数出现了几次。
2、代码实现
def count_sort(li, max_count):
count = [0 for _ in range(max_count+1)] # 初始化计数器列表
for val in li: # 从0开始记录每个数字出现的次数
count[val] += 1
li.clear()
for ind, val in enumerate(count):
for i in range(val):
li.append(ind)
import random
li = [random.randint(0, 20) for _ in range(10)]
print(li)
count_sort(li, 20)
print(li)
计数排序需要知道列表中数的范围,和开辟相应的新内存,使用限制比较大。
三、桶排序
基于计数排序,首先将元素分在不同的桶中,再对每个桶中的元素尽心排序。
代码实现
def bucket_sort(li, n, max_num): # n:分成多少个桶 max_num:知道的最大的数
buckets = [[] for _ in range(n)] # 创建二维列表,表示列表中存有n个桶
for var in li: # 将各元素放到对应的桶里
# 若n=100, max_num=10000
# 此时桶号是0-99,max_num//n表示每个桶里能存多少个元素
# 由于10000//100=100,且没有第100号桶,所以将其存在第99号桶里
i = min(var // (max_num // n), n-1) # i表示var放到几号桶里
buckets[i].append(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
# 桶排序:平均情况时间复杂度O(n+k)
# 最坏情况时间复杂度O(k*n的平方)
# 空间复杂度O(nk)
import random
li = [random.randint(0, 100) for _ in range(1000)]
print(li)
print(bucket_sort(li, 10, 100))
四、基数排序
1、方法
对于列表中的数,根据个位、十位、百位···,进行分桶。例如,一个最大数为三位数的数组,先根据个位数进行分桶,分别分到十个桶里,然后再次对其进行按照十位数进行分桶(不足位的可以看作零处理),此时个位数会在最前面排序好,十位数会向后排序,最后按照百位数进行分桶,则可以得到一个有序的数列。
2、代码实现
def radix_sort(li):
max_num = max(li) # 最大值,根据最大值的位数判断进行几次桶排序
it = 0
while 10 ** it <= max_num: # 判断最大数有几位,如果最大数是4位,此时it=3,进入循环,it+1,10的4次方=10000,推出循环,代表最大数有4位
buckets = [[] for _ in range(10)]
# 根据位数分桶排序
for var in li:
# 例876 取个位(it=0) (876//10**0)%10,取十位(it=1) (876//10**1)%10,取百位(it=0) (876//10**2)%10,
digit = (var // 10**it) % 10
buckets[digit].append(var)
# 分桶完成
li.clear() # 清空原列表
for buc in buckets:
li.extend(buc) # 把数重新写入li
it += 1
# 时间复杂度 O(kn) # k=lgn
# 空间复杂度 O(k+n)
import random
li = [i for i in range(100)]
random.shuffle(li)
print(li)
radix_sort(li)
print(li)