layout: posttitle: 数据结构与算法—快速排序
date: 2019-10-10
tag: 数据结构与算法
分而治之(divide and conquer, D&C)
# 计算列表之和from typing import Listdef sumList(arr:List[int]) -> int:if len(arr) == 0:return 0if len(arr) == 1:return arr[0]else:return arr[0]+sumList(arr[1:])# 计算列表中的元素的个数def num_of_element(li):if len(li) == 1:return 1else:return 1 + num_of_element(li[1:])# 计算列表中的最大值def max_num(li):if len(li) == 2:return max(li)else:return max(li[0], max_num(li[1:]))max_num([1,2,3,5,6,7,8,9,9,11])
快速排序
import random# 快速排序def quickSort(li):if len(li) < 2: # 当数组是空或者只有一个值时,不需要排序,只需返回结果即可return lielse:base = li[0] # 基准值# 使用第一个为基准值,下面就 从 1 开始less = [i for i in li[1:] if i <= base]# 小于等于 要不然有重复的值会被过滤掉greater = [i for i in li[1:] if i > base]return quickSort(less) + [base] + quickSort(greater)sample = [random.randrange(100) for i in range(5)]print(sample)print(quickSort(sample))
# 随机选择基值import random# 快速排序def quickSort(li):if len(li) < 2: # 当数组是空或者只有一个值时,不需要排序,只需返回结果即可return lielse:idx = random.randrange(0, len(li))base = li[idx] # 随机选择基准值less = [i for i in li[:idx] + li[idx + 1:] if i <= base]# 随机选择基准值,索引的时候去掉基准值,小于等于确保重复的值不被过滤greater = [i for i in li[:idx] + li[idx + 1:] if i > base]#print((idx, less, greater))return quickSort(less) + [base] + quickSort(greater)sample = [1, 2, 1, 2, 3]print((quickSort(sample)))
再谈大 O 表示
快速排序的独特之处在于,其速度取决于选择的基准值。

最糟情况和平均情况
以上面的快速排序为例,若递归时基准值总是取最小或者最大,那么它左右两边总有一边是空,一边包含全部值,如下图,这时调用栈的高度会非常大,这是最糟情况。此时该算法的复杂度是 O()

若快速排序算法选择的基准值是每次列表中中间的那一个,那么调用的栈会比较短,每次都将数组分成两半,所以不需要太多次的递归调用就能达到基线条件。此时对应的是最佳情况,时间复杂度为 O()

最佳情况也是平均情况。只要你每次都随机地选择一个数组元素作为基准值,快速排序的平均运行时间就将为 O()。快速排序是最快的排序算法之一,也是 D&C 典范。
小结
D&C 将问题逐步分解。使用 D&C 处理列表时,基线条件很可能是空数组或只包含一个元素的数组。
实现快速排序时,请随机地选择用作基准值的元素。快速排序的平均运行时间为
O(n log n)。大O表示法中的常量有时候事关重大,这就是快速排序比合并排序快的原因所在。
比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时, O(log n) 的速度比 O(n) 快得多。
