Python 排序算法

十大经典排序算法之属性总览

在这里插入图片描述

1. 插入排序

  • 从第二个元素开始和前面的元素进行比较(逆序比较),如果前面的元素比当前元素大,则将前面元素后移一位;当前元素继续往前比较,直到找到比它小或等于它的元素并插入在其后面;然后选择第三个元素,重复上述操作,进行插入,依次选择到最后一个元素,插入后即完成所有排序
def insertionSort(arr):
    for i in range(1, len(arr)):
        curr = arr[i]
        pre_idx = i - 1
        while pre_idx >= 0 and arr[pre_idx] >= curr: # 若前一个元素存在且前一个元素大于当前元素
            arr[pre_idx + 1] = arr[pre_idx]          # 前一个元素后移一位
            pre_idx -= 1                             # 继续向前查找
        arr[pre_idx + 1] = curr                      # 直到找到不大于当前元素的元素,插入其后
    return arr

2. 选择排序

  • 设第一个元素为比较元素,依次和后面的元素比较,比较完所有元素找到最小的元素,将它和第一个元素互换,重复上述操作;找出第二小的元素和第二个位置的元素互换,以此类推找出剩余最小元素将它换到前面,即完成排序
def selectionSort(arr):
    for i in range(len(arr) - 1):                   # 从第一个元素到倒数第二个元素
        min_idx = i                                 # 每轮假设第一个元素为最小元素
        for j in range(i, len(arr)):                # 遍历i之后的所有元素,若存在比当前最小元素更小的元素,则更新
            if arr[min_idx] > arr[j]:
                min_idx = j                         # 更新最小元素下标
        arr[min_idx], arr[i] = arr[i], arr[min_idx] # 每轮结束后,交换当前元素i和找到的最小元素
    return arr

3. 冒泡排序

  • 从第一个和第二个开始比较,如果第一个比第二个大,则交换位置,然后比较第二个和第三个,逐渐往后,经过第一轮后最大的元素已经排在最后;重复上述操作的话第二大的则会排在倒数第二的位置;重复上述操作n-1次即可完成排序,因为最后一次只有一个元素所以不需要比较
def bubbleSort(arr):
    for i in range(len(arr) - 1):                   # 从第一个元素到倒数第二个元素
        for j in range(len(arr) - 1 - i):           # 从第一个元素到未排序的n-1-i个元素,最后i个元素已排序完成
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j] # 相邻两个元素依次顺序对比,若大小顺序相反则交换
    return arr

4. 快速排序

  • 从原数组中随机选择一个值作为中枢值,数组中剩下的每一个元素与中枢值进行比较;小于中枢值的元素存放到一个子数组,大于中枢值的元素存放到另一个子数组,对子数组递归进行上述操作,直至子数组长度为0或1时返回子数组,开始返回递归
def quickSort(arr):
    # 若子数组只含有0个或1个元素,则默认为有序数组,直接返回
    if len(arr) < 2:
        return arr
                                        # 以下3中初始化中枢值的方法,选择一种执行即可
    pivot_idx = 0                       # 1.初始化中枢值为数组arr的第一个元素
    pivot_idx = -1                      # 2.初始化中枢值为数组arr的最后一个元素
    pivot_idx = len(arr) // 2           # 3.初始化中枢值为数组arr的中间元素
    pivot = arr[pivot_idx]              # pivot可以去左右任意一边
    arr.pop(pivot_idx)                  # 去掉中枢元素

    L = [i for i in arr if i <= pivot] # 小于或等于pivot的子数组L(这里将pivot分到左边数组L)
    R = [i for i in arr if i > pivot]  # 大于pivot的子数组R

    return quickSort(L) + [pivot] + quickSort(R) # 返回值:较小值子数组 + 中枢值 + 较大值子数组

5. 归并排序

  • 使用二分法将原数组不断两两划分,直至达到原子数组,然后返回递归;在返回过程中对子数组进行排序,再两两合并,直至合并到原数组长度(分而治之)
def mergeSort(arr):
    # 达到原子数组,直接返回
    if len(arr) < 2:
        return arr

    # 确定中间元素下标,二分法
    mid = len(arr) // 2
    # 根据中间元素将原数组划分为左右两个子数组,两个子数组分别进行归并排序
    L, R = mergeSort(arr[:mid]), mergeSort(arr[mid:])

    # 归并排序需要新建一个辅助数组(空间复杂度高可能就是归并排序不如快速排序的一点吧。。。)
    sorted_arr = []
    # 当左右子数组中均不为空时,进行循环
    while len(L) > 0 and len(R) > 0:
        # 依次顺序比较左右子数组中元素大小,将小的一个存入辅助数组中
        if L[0] > R[0]:
            sorted_arr.append(R.pop(0))
        else:
            sorted_arr.append(L.pop(0))

    # 追加可能剩余的元素
    sorted_arr += L
    sorted_arr += R

    return sorted_arr

6. 堆排序

  • 堆分为最大堆和最小堆,是完全二叉树;堆排序就是把堆顶的最大数取出,将剩余的堆继续调整为最大堆;剩余部分调整为最大堆后,再次将堆顶的最大数取出,再将剩余部分调整为最大堆,这个过程持续到剩余数只有一个时结束
### 递归版本
def heapify_recur(arr, p_idx, heap_size):
    max_idx = p_idx
    l_idx, r_idx = p_idx * 2 + 1, p_idx * 2 + 2

    if l_idx < heap_size and arr[max_idx] < arr[l_idx]:
        max_idx = l_idx

    if r_idx < heap_size and arr[max_idx] < arr[r_idx]:
        max_idx = r_idx

    if max_idx != p_idx:
        arr[p_idx], arr[max_idx] = arr[max_idx], arr[p_idx]
        heapify_recur(arr, max_idx, heap_size)
        
### 循环版本
def heapify(arr, parent_idx, heap_size):
    parent_val = arr[parent_idx]   # 暂存父节点元素,用于和下面的节点进行对比
    child_idx = parent_idx * 2 + 1 # 计算(左)子节点下标

    # 当子节点未越界时,进行循环
    while child_idx < heap_size:
        # 若此父节点存在右子节点,且右子节点大于左子节点,则更新子节点下标为右子节点
        if child_idx + 1 < heap_size and arr[child_idx] < arr[child_idx + 1]:
            child_idx += 1

        # 若父节点大于或等于最大子节点,则可以停止比较,退出循环
        if parent_val >= arr[child_idx]:
            break

        # 若父节点小于最大子节点,则替换父节点和子节点的元素和下标
        arr[parent_idx] = arr[child_idx]
        parent_idx = child_idx
        child_idx = parent_idx * 2 + 1 # 继续循环比较下面的节点

    # 若不存在大于父节点的子节点,则父节点不动;否则将原始的父节点元素下沉到叶子结点
    arr[parent_idx] = parent_val

def heapSort(arr):
    # 第一次对原数组构建最大堆时,因为知道所有的非叶节点,因此可以从最后一个非叶结点(下标为len(arr)//2 - 1)开始向上遍历全部非叶结点,无需从根节点遍历
    for parent_idx in range(len(arr)//2 - 1, -1, -1):
        heapify(arr, parent_idx, len(arr))              # parent_idx表示非叶节点
#         heapify_recur(arr, parent_idx, len(arr))

    # 第二次开始则从根节点构建最大堆(无法直接知道所有非叶节点),且每次减少一个元素
    for heap_size in range(len(arr) - 1, 0, -1):
        arr[0], arr[heap_size] = arr[heap_size], arr[0] # 交换最大根和最后一个元素
        heapify(arr, 0, heap_size)                      # heap_size表示当前待排序堆的数组长度,0表示根节点
#         heapify_recur(arr, 0, heap_size)

    return arr
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值