问题:
用Python实现八种排序算法,对随机产生的500个1000以内的数进行排序,统计不同算法的效率,并将其显示在柱状图或者折线图中。其他图也可以,可视化就行。
这里使用的八种排序算法分别是:冒泡、选择、插入、希尔、快排、归并、基数、堆排
# 用Python实现四种排序算法,对随机产生的500个1000以内的数进行排序
# 八大排序算法:冒泡、选择、插入、希尔、快排、归并、基数、堆排
import numpy as npy
# 交换列表中两个数的方法
def swap(my_list: list, a: int, b: int):
"""
:param my_list: 列表
:param a: 第一个数的下标
:param b: 第二个数的下标
:return:
"""
temp = my_list[a]
my_list[a] = my_list[b]
my_list[b] = temp
# 冒泡排序
def bubble_sort(my_list: list) -> list:
"""
冒泡排序:依次比较相邻两个数大小,如果错误,则交换顺序
优化:增加一个bool类型变量,如果没有进行一次交换,说明排序已完成,无需继续
:param my_list: 待排序的列表
:return: 排序完成完成的列表
"""
for i in range(len(my_list)-1):
is_change = False # 用于判断是否进行了交换
for j in range(len(my_list)-1-i):
if my_list[j] > my_list[j+1]:
is_change = True
swap(my_list, j, j+1)
if not is_change: # 优化
break
return my_list
# 选择排序
def select_sort(my_list: list) -> list:
"""
选择排序:
1.假定第一个为最小数,依次与后面的数比较,如果发现更小的数,记录其下标再让其作为最小值,继续与后面的数进行比较
直到最后
2.第一轮比较结束后,通过记录的最小数的下标,让最小数与假定的第一个数进行交换
3.假定第二个数、第三个数...倒数第二个数重复以上步骤
优化:如果记录的下标等于假定数的下标,则不用交换
:param my_list: 待排序的列表
:return: 排序完成完成的列表
"""
for i in range(len(my_list)-1):
min_index = i # 假定最小数的下标
min_num = my_list[i] # 假定最小数
for j in range(i+1, len(my_list)):
if min_num > my_list[j]: # 找到更小的数
min_index = j
min_num = my_list[j]
if min_index != i: # 优化
my_list[min_index] = my_list[i]
my_list[i] = min_num
return my_list
# 插入排序
def insert_sort(my_list: list) -> list:
"""
插入排序:
1.从第二个数开始,用一个变量temp存放这个数,与前面的数依次进行比较,如果比当前数大,直接进行交换继续比较,如果比temp小,则结束
2.第二个数到最后一个数重复第一步
:param my_list: 待排序的列表
:return: 排序完成完成的列表
"""
for i in range(1, len(my_list)):
temp = my_list[i] # 辅助变量,用于存放要找适当位置的数
index = i-1 # 辅助指针
while index > 0:
if my_list[index] > temp:
my_list[index + 1] = my_list[index]
else:
break
index -= 1
if i != index+1:
my_list[index+1] = temp
return my_list
# 希尔排序
def shell_sort(my_list: list) -> list:
"""
希尔排序:
1.对数列进行分组,len/2、len/2/2...1 直到len/2=1
2.每次分完组,都对本组的数列进行插入排序
:param my_list: 待排序的列表
:return: 排序完成完成的列表
"""
step = int(len(my_list) / 2)
while step > 0:
i = step
while i < len(my_list):
temp = my_list[i]
index = i-1
while index > 0:
if my_list[index] > temp:
my_list[index+1] = my_list[index]
else:
break
index -= 1
if index+1 != i:
my_list[index+1] = temp
i += 1
step /= 2
step = int(step) # 进行取整
return my_list
# 快速排序
def quick_sort(my_list: list, left: int, right: int) -> list:
"""
快速排序:
基本思想就是分治,每次其实就是进行填坑(将比第一次坑中的数即要处理的数大的放右边,小的放左边)
如: 1.先定好左指针(left)和右指针(right),用一个辅助变量temp记录要处理的数
2.假设第一个就是要处理的数(第一次坑中的数),从right开始向左找,找到比temp小的数就停止,然后把这个比temp小的数放入坑中,此时坑变到比temp小的数的位置上,同时让left+1
3.从left开始向右找,找到比temp大的数就停止,然后把这个比temp大的数放入第二步的新坑中,此时坑变到比temp大的数的位置上,同时让right-1
4.重复第2、3步,直到left=right,这个位置就是最后一个坑了,将temp放入最后一个坑中,即left或right的位置,此时temp左边都是比它小的数,右边都是比它大的数,所有坑都填满了
:param my_list: 待排序的列表
:param left: 最左边的下标
:param right: 最右边的下标
:return: 排序完成的列表
"""
if left < right:
l = left
r = right
temp = my_list[l] # 辅助变量,挖出的坑
while l != r:
while l != r:
if my_list[r] < temp:
my_list[l] = my_list[r]
l += 1
break
r -= 1
while l != r:
if my_list[l] > temp:
my_list[r] = my_list[l]
r -= 1
break
l += 1
my_list[l] = temp
quick_sort(my_list, left, l-1)
quick_sort(my_list, l+1, right)
return my_list
# 归并排序
def merge_sort(my_list: list, left: int, right: int, temp_list: list) -> list:
"""
归并排序:
基本思想就是先分再合,分是自上而下分,分到不能再分了,如只有两个元素,合是从分得不能再分自下而上进行合并
合并的具体思想是:利用一个辅助列表,再利用两个指针left(0<=left<=mid)和right(mid+1<=right<=数列长度-1),
将指针所指向的更小的数填入到辅助列表
:param my_list: 待排序的列表
:param left: 最左边的下标
:param right: 最右边的下标
:param temp_list: 辅助列表
:return: 排序完成完成的列表
"""
if left < right:
mid = int((left + right)/2) # 要进行取整
merge_sort(my_list, left, mid, temp_list) # 向左分
merge_sort(my_list, mid+1, right, temp_list) # 向右分
l = left # 左指针
r = mid+1 # 右指针
t = 0 # 辅助列表的辅助指针
while l <= mid and r <= right:
if my_list[l] < my_list[r]:
temp_list[t] = my_list[l]
t += 1
l += 1
else:
temp_list[t] = my_list[r]
t += 1
r += 1
while l <= mid:
temp_list[t] = my_list[l]
t += 1
l += 1
while r <= right:
temp_list[t] = my_list[r]
t += 1
r += 1
t = 0 # 让辅助列表的辅助指针归零
t2 = left # 待排序列表的辅助指针
while t2 <= right:
my_list[t2] = temp_list[t]
t2 += 1
t += 1
return my_list
# 基数排序
def radix_sort(my_list: list) -> list:
"""
类似桶排序
:param my_list: 待排序的列表
:return: 排序完成的列表
"""
# 创建10个桶每个桶的大小为待排序列表的元素个数
barrels = [[0 for i in range(len(my_list))] for i in range(10)]
count = [0 for i in range(10)] # 用于记录每个桶中数的个数
# 首先,找到待排序列表最大数的位数
max_num = max(my_list)
digit = len(str(max_num)) # 待排序列表中的最大位数
for i in range(digit):
# 将所有数放入对应的桶中
for j in range(len(my_list)):
mold = int(my_list[j] / 10**i % 10) # 取模的结果代表放入第几号桶
barrels[mold][count[mold]] = my_list[j]
count[mold] += 1
# 将桶中的数依次放入待排序列表中
t = 0 # 辅助指针
for j in range(10):
if count[j] != 0: # 如果桶不为空
for k in range(count[j]): # 取出桶中的数
my_list[t] = barrels[j][k]
t += 1
count[j] = 0 # 取完后,桶置空
return my_list
# 获得小的大顶堆的方法
def get_small_heap(my_list: list, index: int, length: int):
"""
:param my_list: 待排序的列表
:param index: 非叶子节点的下标
:param length: 列表长度
:return:
"""
left = 2*index+1 # 非叶子节点的左子节点
while left < length:
right = left+1 # 右子节点
if right < length and my_list[right] > my_list[left]: # 如果右子节点存在并且大于左子节点
left = right
if my_list[index] < my_list[left]: # 将大的子节点作为父节点
swap(my_list, index, left)
index = left
else:
break
left = 2*left+1
# 堆排序
def heap_sort(my_list: list) -> list:
"""
堆排序:
1.先构建大顶堆
2.然后将堆顶的数与最后一个数进行交换,此时最后一个数就是最大的,可以排除最后一个元素了,此时只需要将堆顶的元素放到合适的位置
3.将堆顶的数自上而下放到合适的位置
4.重复2、3步,直到剩下最后一个元素
:param my_list: 待排序的列表
:return: 完成排序的列表
"""
length = len(my_list)
index = int(length / 2 - 1) # 最后一个非叶子节点(要转为整数)
while index >= 0:
get_small_heap(my_list, index, length)
index -= 1
while length > 0:
swap(my_list, 0, length-1) # 先将最后一个数与堆顶的数进行交换
length -= 1
get_small_heap(my_list, 0, length) # 将堆顶的数放到合适的位置
return my_list
def main():
print("20个随机数的8种排序结果如下")
list1 = list(npy.random.randint(500, 10001, 20))
print('待排序的列表:', list1)
# 冒泡排序结果
result1 = bubble_sort(list1)
print('冒泡排序结果:', result1)
# 选择排序结果
result2 = select_sort(list1)
print('选择排序结果:', result2)
# 插入排序结果
result3 = insert_sort(list1)
print('插入排序结果:', result3)
# 希尔排序结果
result4 = shell_sort(list1)
print('希尔排序结果:', result4)
# 快速排序结果
result5 = quick_sort(list1, 0, len(list1)-1)
print('快速排序结果:', result5)
# 归并排序结果
list2 = [0 for i in range(len(list1))]
result6 = merge_sort(list1, 0, len(list1)-1, list2)
print('归并排序结果:', result6)
# 基数排序结果
result7 = radix_sort(list1)
print('基数排序结果:', result7)
# 堆排序结果
result8 = heap_sort(list1)
print('堆排序的结果:', result8)
if __name__ == '__main__':
# 展示8种排序对20个数排序后的结果
main()
结果:
由于冒泡、选择、插入排序数据量大的时候效率特别低,所以不考虑
希尔排序 | 快速排序 | 归并排序 | 基数排序 | 堆排序 | |
---|---|---|---|---|---|
8w数据 | 20ms左右 | 15-35ms | 20ms内 | 50ms内 | 20ms内 |
80w 数据 | 200ms左右 | 100ms左右 | 130ms内 | 100ms内 | 150ms内 |
800w数据 | 2-3s | 1s左右 | 5s内 | 500ms内 | 2s左右 |
import numpy as npy
import matplotlib.pyplot as plt
def draw():
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 由于冒泡、选择、插入排序数据量的的时候效率特别低,所以不考虑
x = npy.array(range(1, 13, 4))
x_ticks = [8, 80, 800]
colors = ['red', 'yellow', 'blue', 'green', 'purple']
labels = ['希尔排序', '快速排序', '归并排序', '基数排序', '堆排序']
# 测试8万、80万、800万个数,得出以下数据
y1 = [21, 201, 2123]
y2 = [32, 103, 997]
y3 = [19, 112, 1375]
y4 = [46, 98, 467]
y5 = [18, 123, 2011]
plt.bar(x=[i-1.2 for i in x], height=y1, width=0.6, color=colors[0], label=labels[0])
plt.bar(x=[i-0.6 for i in x], height=y2, width=0.6, color=colors[1], label=labels[1])
plt.bar(x=x, height=y3, width=0.6, color=colors[2], label=labels[2])
plt.bar(x=[i+1.2 for i in x], height=y4, width=0.6, color=colors[3], label=labels[3])
plt.bar(x=[i+0.6 for i in x], height=y5, width=0.6, color=colors[4], label=labels[4])
plt.title('5种算法耗费的时间统计图')
plt.xlabel('数据量/w')
plt.ylabel('时间/ms')
plt.legend()
plt.xticks(x, x_ticks)
plt.show()
if __name__ == '__main__':
draw()