快速排序,说白了就是给基准数据找其正确索引位置的过程。
快速排序的基本思想是:通过一次排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一不部分的所有数据都要小,然后再按次方法对这两部分数据分别进行快速排序,整个排序过程可以递归或者非递归进行,以此达到整个数据变成有序序列。
像合并排序一样,快速排序也是基于分治模式的。
- 在数据集之中,选择一个元素作为”基准”(pivot)(第一个元素,最后一个元素,中间元素,随机选择)
- 所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边
- 对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止
一个例子
现在有一个数据集{85, 24, 63, 45, 17, 31, 96, 50}
第一步,选择中间的元素45作为”基准”。(基准值可以任意选择,但是选择中间的值比较容易理解。)
第二步,按照顺序,将每个元素与”基准”进行比较,形成两个子集,一个”小于45”,另一个”大于等于45”。
第三步,对两个子集不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
实现
快速排序
1 | quick_sort(array,left,right) |
数组划分
1 | partition(array,left,right) |
选取枢纽元问题
糟糕的方法
通常的做法是选择数组中第一个元素作为枢纽元,如果输入是随机的,那么这是可以接受的。但是,如果输入序列是预排序的或者是反序的,那么依据这样的枢纽元进行划分则会出现相当糟糕的情况,因为可能所有的元素不是被划入S1
,就是都被划入S2
中。
较好的方法
一个比较好的做法是随机选取枢纽元,一般来说,这种策略是比较妥当的。
三数取取中值方法
例如,输入序列为 8, 1, 4, 9, 6, 3, 5, 2, 7, 0 ,它的左边元素为8,右边元素为0,中间位置|_left+right)/2_|
上的元素为6,于是枢纽元为6.显然,使用三数中值分割法消除了预排序输入的坏情形,并且减少了快速排序大约5%(此为前人实验所得数据,无法具体证明)的运行时间。