快速排序及其Python实现【图解算法】
1. 引出问题
假设你的手机中存储了很多乐曲,对于每个乐队,你都记录了其作品被播放的次数。
乐队 | 播放次数 |
---|---|
RADIOHEAD | 156 |
KISHOKE KUMAR | 141 |
THE BLACK KEYS | 35 |
NEUTRAL MILK HOTEL | 94 |
BECK | 88 |
THE STROKES | 61 |
WILCO | 11 |
你想将乐队按照播放次数从多到少进行排列,从而将你喜欢的乐队排序,怎么做呢?
2. 选择排序
我们最初的思路可能类似于选择排序,即每次都选择播放量最高的乐队转移进一个新列表,直到旧列表中没有任何数据。
新列表就是排序完成的结果,算法链接为:选择排序
3. 快速排序
快速排序的核心思想是分而治之(D&C:divide and conquer),是递归式解决方案。
D&C解决问题包括两个步骤:
(1)找出基线条件,这种条件必须尽可能简单
(2)不断将问题分解(或者说缩小规模),直至其符合基线条件。
基线条件就是停止递归(或者说循环)的条件,这里排序最简单的条件是什么呢?大家看这样的数组:
数组 | 解释 |
---|---|
[] | 空数组 |
[20] | 只包含一个元素的数组 |
这两种情况,我们需要对数组进行排序吗?当然不!
思路来了,我们要把数组分解成类似上面的两种情况,首先看到我们的数组:
进行第一步分解,选择中间位置 location=ceil(len(arr)/2)=4,这个值是94。
(注意1:因数组本身是无序的,所以选择的元素位置并没有规定,随便选,这里只是为了便于理解)
这里的94即是我们第一步分解的基准值,我们将其他元素与之比较大小,建立两个新的子数组arr1和arr2:
可以看到,这两个子数组并没有排序,只是按照大于或小于35进行了分类,我们继续分解(以arr2为示例):
arr2,选择中间位置 location=ceil(len(arr2)/2)=1,因此基准值是156,再次进行分类:
这里我们把arr2分为了arr3和arr4,这两个结果就符合我们的基线条件,不需要任何排序操作,返回原来的值即可。
同样的把arr1分解之后,如果不符合基线条件,则继续分解,直至符合基线条件。
这就是整个快速排序的算法流程,核心就是分而治之和基线条件。
我们执行了多少次元素检查呢?
最差情况下,我们每次选择的基准值都使一个子数组为空,需要执行n+(n-1)+…+1次检查,和选择排序一致,为O(n²)。
最好情况下,我们选择的基准值都使子数组元素数量一致,执行n+n-1+…,共ceil(log(n))层检查,时间为O(n×log n)。
4. Python代码(升序排列)
键入以下代码并运行:
uickSort函数作用:对数组进行快速排序(降序)
## 输入:
## array:传入的数组
## 输出:
## return :完成排序的数组
def quickSort(array):
if len(array)<2: # 基线条件判断,如果符合,直接返回
return array
else:
pivot=array[0] # 选择基准值,这里选的第一个值,随便选都行
greater=[i for i in array[1:] if i>pivot] # 找到大于基准值的数,放入子数组1
less=[i for i in array[1:] if i<=pivot] # 找到小于基准值的数,放入子数组2
return quickSort(greater)+[pivot]+quickSort(less) # 进行迭代递归,直至所有子数组达到基线条件
## 测试代码
print(quickSort([156,141,94,88,61,35,11]))
这里对代码进行了详尽的注释,因此不再赘述,结果如下: