01
计数排序
基本思想:
计数排序不是一个比较排序算法,该算法于1954年由 Harold H. Seward提出,通过计数将时间复杂度降到了O(N)。计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
步骤:
1.找出待排序的数组中最大元素
2.统计数组中每个值为i的元素出现的次数,存入数组count的第i项
3.对所有的计数累加(从count中的第一个元素开始,每一项和前一项相加)
4.反向填充目标数组,将每个元素i放在新数组的第count[i]-1项(从0开始),每放一个元素就将count[i]减去1
例如有10个年龄不同的人,统计出有8个人的年龄比A小,那A的年龄就排在第9位,用这个方法可以得到其他每个人的位置,也就排好了序。
def countingSort(arr, max):
# 初始化计数组
result = [0 for i in range(0, len(arr))]
count = [0 for i in range(0, max + 1)]
# 对应下标进行计数
for i in arr:
count[i] += 1
# 计算位置
for i in range(1, len(count)):
count[i] = count[i - 1] + count[i]
# 排序
for i in arr:
result[count[i] - 1] = i
count[i] -= 1
return result
arr = [3, 6, 2, 4, 2, 13, 9, 21, 11]
print(countingSort(arr, 21))
输出结果:
[2, 2, 3, 4, 6, 9, 11, 13, 21]
平均时间复杂度:O(n + k)
空间复杂度:O(k)
稳定性:稳定
02
桶排序
由于桶排序算法把每个数都放到合适的“桶”里进行排序,因此而得名。桶排序的算法原理可以理解为创建一个新的数组,把数依次放入合适的桶内,再按一定顺序输出桶。
当每个桶的数据范围为 1 且数据皆为整数时,桶排序的时间复杂度在所有情况下都是 O(n),因为它是一个线性的排序算法。但是,它的空间需求要视排序数据的范围而定,所以极有可能浪费很多空间。
桶排序实质上与计数排序差异不大。
计数排序是假设输入的数据都属于一个小区间内的整数,而桶排序则假设输入是由一个随机过程产生的。该过程将元素均匀、独立的分布在区间上。
import random
def getInfo(arr):
max = arr[0]
min = arr[0]
for item in arr:
max = item if item > max else max
min = item if item < min else min
return min, max
def bucketSort(arr):
min, max = getInfo(arr)
s = [0 for i in range(0, max - min + 1)]
for item in arr:
s[item - min] += 1
current = min # 记录最小值
index = 0 # 用于指示 arr中的索引
# 循环新数组,将对应的索引位置的数取出
for i in s:
# 要循环i的值,因为i代表这个数字重复几次
while i > 0:
arr[index] = current
i -= 1 # 当有两个重复值的时候起作用
index += 1
current += 1
return arr
arr = [random.randint(0, 100) for i in range(10)]
print(bucketSort(arr))
输出结果:
[11, 24, 26, 48, 50, 68, 75, 90, 92, 95]
平均时间复杂度:O(n + k)
空间复杂度:O(n + k)
稳定性:稳定
03
基数排序
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。
import random
def radixSort(arr, length):
# 根据位数来循环
for k in range(length):
# 生成十进制的一个二维数组,用于存每一位的数字
s = [[] for i in range(10)]
# 将每个数字i存到这个二维数组中对应的位置
# i//(10**k) 表示数字的第几位
# 上面的结果 %10,再利用数组的append存进去
for i in arr:
s[i // (10 ** k) % 10].append(i)
# 本次循环的结果存入到oldlist中
arr = [a for b in s for a in b] # 双重循环
return arr
arr = [random.randint(0, 1000) for i in range(10)]
print(radixSort(arr, 4))
输出结果:
[71, 342, 446, 501, 652, 679, 709, 762, 825, 915]
平均时间复杂度:O(n x k)
空间复杂度:O(n + k)
稳定性:稳定
04
总结
这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异。
排序算法 | 对桶的使用 |
计数排序 | 每个桶只存储单一键值 |
桶排序 | 每个桶存储一定范围的数值 |
基数排序 | 根据键值的每位数字来分配桶 |