3-2 快速排序讨论
3-2 快速排序讨论
目录
小节 | 位置 |
---|---|
3.2.1 | 基础问题-分块操作 |
3.2.2 | 经典快速排序问题 |
3.2.3 | 荷兰国旗问题 |
3.2.4 | 利用荷兰国旗问题改进快 |
3.2.5 | 快速排序再改进 |
3.2.1 基础问题-分块操作
分块问题(Partition 问题):
给定一个数组arr,和一个数num,请把小于 num的数放在数组的左边,大于等于num的数放在数组的右边。
要求额外空间复杂度O(1),时间复杂度O(N)。
- 解决思路_1:
设立一个游标,该游标可以从左向右逐渐移动,移动的条件为,将数组中小于num的数,不断的插入到游标的位置,然后游标右移。
这样,游标的左侧区域就逐渐扩张,相当于顶着游标在往右移动。
最终,当所有元素都考察过之后,游标左侧区域的数,一定是小于num,游标右侧的数大于等于num。
def Swap(a, i, j): |
- 解题思路_2: 双指针法
上面的方案,设立了一个游标 L 从左往右移动,使得 L 左侧的值小于base。但是i要从左往右遍历一次,速度不够快。
这里可以改进一下,再增加一个游标 R,让其从右往左移动,使得 R 右侧的数,大于等于base。
当L和R撞在一起的时候,就可以结束了。
def Partition_2(a): |
3.2.2 经典快速排序问题
在了解了上面的分区域(partition)操作之后,就已经可以将其应用到快速排序中了。
快速排序其实也是一种分而治之思想在排序算法上的典型应用,而且使用了递归的方法。
不断的将数组分为更小的两块区域,使其在最小的区域上能有序,进而在全局都有序。
def QuickSort_1(a, L, R): |
3.2.3 荷兰国旗问题
之前的Partition操作只要求分为两块区域,一部分小于等于,另一部分大于(或者一部分小于,另一部分大于等于)。
会发现,等于基准base的数,并不一定处于中间位置,尤其当有很多个数等于base的时候。
那么如果现在分为三块区域,即【小于,等于,大于】,就能加速排序过程。
举个例子,比如三个区域各有 N/3个数,那么下一轮迭代,就不用考虑等于区域的数了,就减少了计算量。
所以三块区域的 partition 问题,被称为【荷兰国旗问题】。
(可是荷兰国旗明明是横着的三块区域,我也不知道为什么当初人们起这个名字,我觉得更像法国国旗……)
def Partition_3(a): |
3.2.4 利用荷兰国旗问题改进快速排序
快速排序的架构其实很简单,就是先Partition,然后递归左右两侧区域,
这里我们只需要把 Partition 的方法换成 3区域 的方法即可:
def QuickSort_2(a, L, R): |
3.2.5 快速排序再改进
前面的所有 Partition 方法,都是固定了一个位置的数,作为base来进行比较,大多数时候我们习惯直接用最右侧的数。
但是这样就有一个问题,那就是当数据处于某一种情况的时候,每一次的取右侧那个数作为基准,可能都会导致有较多的swap操作。
输入数据是我们不可控的,为了使得算法稳定,我们可以使得每一次的key,都是随机产生的,这样从统计学角度讲,会是比较稳定快速排序。
import random |