Python3实现快速排序
写在前面
在网上看了很多五花八门的快速排序,当然这些排序的时间和空间复杂度都不一样,难以衡量其好还是坏。但是大多都比较难理解。我这里根据快排的思想,用最简洁的、最容易理解的代码实现了快排。
快排思想(分治思想)
- 从要排序的arr里随机找一个基准值p,根据基准值p把arr分解到两个arr里,分别是arr1、arr2。分解时要保证arr1里的所有数据小于arr2(不需要保证这两个arr里的数据有序);
- 从arr1里随机找出一个元素当做基准值p,根据p的值把arr1分解成arr11/arr12,分解时要保证arr11里的数据小于arr12里的所有数据(不需要保证两个arr里的数据有序);
- 同理,从arr2里随机找出一个元素当做基准值p,根据p的值把arr2分解成arr21/arr22 …
- 就这样不断递归分解下去,最后每个子arr里只有一个元素,再把这些元素合并,最后就得到一个有序的数组。
递归拆解
对于快排来说,其思想并不难理解。对于代码实现来说,最难的就是递归部分,下面通过一个实例来帮助理解递归的过程。
假设我需要排序的数组如下
arr = [95, 20, 10, 15, 8, 24, 57, 60, 15, 88, 92]
这里为了简单理解,每次找基准值我都取数组的最后一位。所以这里我选92,通过第一轮拆解数组就变为如下两个
arr1 = [20, 10, 15, 8, 24, 57, 60, 15, 88]
p = [92]
arr2 = [95]
arr2只有一个元素95,不需要拆解。
所以,这里对arr1拆解为如下两个数组,选取最后一个元素88来当做基准值
arr11 = [20, 10, 15, 8, 24, 57, 60, 15]
p = [88]
arr12 = []
arr12不需要拆解
对arr11拆解为如下两个数组,选取最后一个元素15来当做基准值
arr111 = [10, 15, 8]
p = [15]
arr112 = [20, 24, 57, 60]
同理,对arr111拆解为
arr1111 = []
p = [8]
arr1112 = [10, 15]
对arr112进行拆解 …
最后,arr的子数组里都只有0个或者1个元素,就不需要再比较了。此时得到的数组就是有序的。
代码实现
def quick_sort(arr):
if len(arr) <= 1: # 递归终止条件 数组中只有一个元素
return arr
x = arr[-1] # 获取分区节点,我这里直接取最后一个
arr.pop(-1) # 记得要把分区节点的数据删除,不参与遍历
left, right = [], []
for i in arr: # 大于分区节点的数据放到右边的数组里 ,小于或等于分区节点的数据放到左边的数组里
left.append(i) if i <= x else right.append(i)
return quick_sort(left) + [x] + quick_sort(right) # 再递归对左右两边的数据进行拆分
性能分析(缺点)
对100万个数据(负1亿至正1亿之间的随机数),花了不到5秒的时间。
优点就不说了,大家一看就懂,这里说说缺点:
- 空间复杂度高O(n);除了存储数据本身的内存外,还需要额外的空间来存储left、right两个数组的值;
- 时间复杂度相对较高;因为取的基准值是最后一位,所以非常容易导致左右两边的数组失衡。最坏的情况下,时间复杂度退化到O(n^2)。
来源:CSDN
作者:Alibins
链接:https://blog.csdn.net/qq_41311882/article/details/103531522