[算法] 各大排序算法的python实现

排序算法总结:

图片名词解释:

  • n: 数据规模
  • k: “桶”的个数
  • In-place: 占用常数内存,不占用额外内存
  • Out-place: 占用额外内存

该节参照:https://blog.csdn.net/weixin_41190227/article/details/86600821

一、快速排序

1.递归形式(一)

# 快速排序递归玩法
def quicksort(arr):
    # 结束条件
    if len(arr) <= 1:
        return arr
    # 用中间值更稳定
    middle = arr[len(arr) // 2]
    # 记得将middle拿出来,最后再放到中间
    del arr[len(arr) // 2]
    # 利用列表生成式,100W数据,2.9s
    # left = [x for x in arr if x <= middle]
    # right = [x for x in arr if x > middle]

    # 2.6s
    left = []
    right = []
    for x in arr:
        left.append(x) if x <= middle else right.append(x)

    # 记得把middle放中间
    return quicksort(left) + [middle] + quicksort(right)


if __name__ == '__main__':
    arr = list(range(100000))
    import random

    random.shuffle(arr)

    sorted = quicksort(arr)
    print(sorted)

2.递归形式(二)

# partition(data,left,right)
def partition(data, left, right):
    tmp = data[left]  # 取最左边的数作为mid
    while left < right:  # 左指针小于右指针时循环,左右重叠终止
        while left < right and data[right] >= tmp:  # 当right数大于mid,则right--
            right -= 1
        data[left] = data[right]  # 当right数小于mid,则将right数放到左边left位置
        while left < right and data[left] <= tmp:  # 当left数小于mid,则left++
            left += 1
        data[right] = data[left]  # 当left数大于mid,则将left数放到右边right位置
    data[left] = tmp  # 当left与right重叠后,重叠的位置就是mid的位置,将tmp覆盖在此位置
    return left  # 返回mid数的idx(left和right一样)


def quicksort2(data, left, right):
    if left < right:  # 当left<right时才递归
        mid = partition(data, left, right)  # 获取中间数的idx,idx左边的数都小于mid,右边的都大于mid
        # 分别对两边递归排序
        quicksort2(data, left, mid - 1)
        quicksort2(data, mid + 1, right)


if __name__ == '__main__':
    arr = list(range(100000))
    import random

    random.shuffle(arr)

    quicksort2(arr, 0, len(arr) - 1)
    print(arr)

3.非递归形式

# 非递归玩法
def quicksort2(arr):
    '''
    模拟栈操作实现非递归的快速排序
    '''
    if len(arr) < 2:
        return arr
    stack = []
    stack.append(len(arr) - 1)
    stack.append(0)
    while stack:
        l = stack.pop()
        r = stack.pop()
        index = partition(arr, l, r)
        if l < index - 1:
            stack.append(index - 1)
            stack.append(l)
        if r > index + 1:
            stack.append(r)
            stack.append(index + 1)


def partition(arr, start, end):
    # 分区操作,返回基准线下标
    pivot = arr[start]
    while start < end:
        while start < end and arr[end] >= pivot:
            end -= 1
        arr[start] = arr[end]
        while start < end and arr[start] <= pivot:
            start += 1
        arr[end] = arr[start]
    # 此时start = end
    arr[start] = pivot
    return start


if __name__ == '__main__':
    li = list(range(1000000))
    import random

    random.shuffle(li)
    sorted = quicksort2(li)

二、插入排序

# 插入排序
def insertsort(arr):
    count = len(arr)

    # 外层循环每循环一次,就将一个未排序的数插入前面排好的队列中
    for i in range(1, count):
        j = i
        # 内存循环,一个一个比较
        while j >= 1:
            # 如果后面比前面小,则交换位置
            if arr[j] < arr[j - 1]:
                arr[j - 1], arr[j] = arr[j], arr[j - 1]
                j -= 1
            else:
                break


if __name__ == '__main__': 
    arr = list(range(10000))
    import random

    random.shuffle(arr)
    insertsort(arr)

三、冒泡排序

# 冒泡排序
def bubblesort(arr):
    count = len(arr)
    # 外层循环每循环一次,就在arr的最后排好一个大值
    for i in range(0, count):
        # 内层循环每循环一次,就是两个相邻数比较,前面大则交换,后面大则不变
        for j in range(0, count - 1 - i):
            if arr[j] > arr[j + 1]:
                # 交换前后两个值
                arr[j], arr[j + 1] = arr[j + 1], arr[j]


if __name__ == '__main__':
    arr = list(range(10000))
    import random

    random.shuffle(arr)
    bubblesort(arr)

四、选择排序

# 选择排序
def selectionsort(arr):
    count = len(arr)
    # 找到列表中的最小值,排到前面
    for i in range(0, count - 1):
        # 初始化剩余未排部分的最小值为第一个数
        min_idx = i
        # 找到剩余未排部分的最小值的索引
        for j in range(i + 1, count):
            if arr[j] < arr[min_idx]:
                min_idx = j
        # 交换找到的最小值与第一个值
        arr[i], arr[min_idx] = arr[min_idx], arr[i]


if __name__ == '__main__':
    arr = list(range(1000))
    import random

    random.shuffle(arr)
    selectionsort(arr)

五、归并排序

# 归并算法,将两个有序列表合并成一个有序列表
def merge(left, right, arr):
    length = len(arr)
    # 获取了总长度后,就清空,用于存放排好序的数据
    arr.clear()
    i, j = 0, 0
    # i和j索引加起来的值不能大于length-2
    while i + j <= length - 2:
        # 谁小就先放入arr中
        if left[i] < right[j]:
            arr.append(left[i])
            # 如果是left的最后一个元素,则将right中剩下的全部放入arr,包括当前right[j]
            if i == len(left) - 1:
                arr.extend(right[j:])
                # 退出循环,排序完毕
                break
            # 如果不是left的最后一个元素,则i++
            i += 1
        else:
            arr.append(right[j])
            # 如果是right的最后一个元素,则将left中剩下的全部放入arr,包括当前left[i]
            if j == len(right) - 1:
                arr.extend(left[i:])
                break
            # 如果不是right的最后一个元素,则j++
            j += 1


# 归并排序,利用分治法,递归的进行归并
def mergesort(arr):
    # 当列表只剩1个元素的时候,不需要排序
    if len(arr) < 2:
        return
    # 获取中间元素的idx
    mid_idx = len(arr) // 2
    # 将列表切成两份(拷贝)
    left = arr[:mid_idx]
    right = arr[mid_idx:]
    # 各自排序
    mergesort(left)
    mergesort(right)
    # 归并起来
    merge(left, right, arr)


if __name__ == '__main__':
    arr = list(range(100000))
    import random

    random.shuffle(arr)

    mergesort(arr)

六、希尔排序

希尔排序是直接插入排序的优化版本,分组进行直接插入排序,逐渐减小组的数量(减小步长),直到只剩1组。

# 希尔排序(shell sort)
def shellsort(arr):
    # 获取总长度
    alen = len(arr)
    n = alen
    # 每次循环步长都小一半
    while n > 0:
        # 计算该次的步长
        n = n // 2
        # 一共有n组,元素之间的步长也是n
        for i in range(n):
            # 每一组有alen//n个元素(最后一组可能是alen//n+1个)
            for j in range(i, alen, n):
                # 以下过程就是直接插入排序
                temp = arr[j]
                if temp < arr[j - n]:
                    while temp < arr[j - n] and j > 0:
                        arr[j] = arr[j - n]
                        j = j - n
                    arr[j] = temp


if __name__ == '__main__':
    arr = list(range(100000))
    import random

    random.shuffle(arr)

    shellsort(arr)

七、堆排序

堆排序的步骤:

1.生成堆

2.从根节点出数

3.将最后一个位置的数放到根节点

4.调整堆

5.循环2-4

# 调整堆
def sift(data, low, high):
    # low为开头,high为结尾
    i = low  # 根节点的idx为low
    j = 2 * i + 1  # 根节点的左子节点的idx为2*i+1
    temp = data[i]  # 获取根节点
    while j <= high:  # 说明根节点有子节点
        if j < high and data[j] < data[j + 1]:  # 根节点有右节点,然后比较左右子节点大小
            j += 1  # 如果右节点大,则j为右节点
        if temp < data[j]:  # 如果根节点小于子节点中较大者
            data[i] = data[j]  # 将大的子节点的值替换到根节点,此时子节点空出来
            i = j  # 将该空出来的位置变为根节点
            j = 2 * i + 1  # 继续计算新的根节点的子节点,准备下次循环
        else:
            break  # 如果根节点大于子节点,则停止循环
    # 退出循环后,将temp里的值(也就是最上层根节点的值)放入空位中
    data[i] = temp


def heapsort(data):
    n = len(data)
    # 生成堆
    for i in range(n // 2 - 1, -1, -1):  # 从最后一个有子节点的父节点开始,调整到根节点
        sift(data, i, n - 1)
    # 出数
    for i in range(n - 1, -1, -1):  # 从最后一个数开始循环
        data[0], data[i] = data[i], data[0]  # 交换最后一个数和根(根最大),相当于将排好序的数放在最后,堆的范围减小1
        sift(data, 0, i - 1)  # 将最后一个数交换到根,然后进行一次调整


# 时间装饰器
def timewrap(func):
    import time
    def warp(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        end = time.time()
        print(end - start)

    return warp


@timewrap
def apply_sort_func(arr):
    heapsort(arr)


if __name__ == '__main__':
    arr = list(range(1000000))
    import random

    random.shuffle(arr)

    apply_sort_func(arr)
    print(arr)

≧◔◡◔≦

原文地址:https://www.cnblogs.com/leokale-zz/p/12174207.html