Python实现八大排序算法及所耗时间的柱形图

问题:
用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()

在这里插入图片描述

发布了35 篇原创文章 · 获赞 15 · 访问量 3968

猜你喜欢

转载自blog.csdn.net/qq_43598193/article/details/104637985