基本排序算法总结与对比 之五 ——快速排序
1、快速排序 二路双向快排 版本
首先列出教科书上最普遍的版本:二路快排,准确的说 应该叫 二路双向快排。笔者认为这个应该是效率最为均衡的版本,网络上也流传着 三路快排,但是 三路快排 仅仅是在序列中存在大量重复元素时,才会发挥出其优势。在对 随机序列 和 近有序序列排序时,效率均低于 二路双向快排。
二路双向快排过程如下图:从左右两端开始检测元素,左边 ≥ 基准元素(基准元素一般选首元素) 的元素 与 右边 ≤ 基准元素 的元素交换,直至两边检查的下标(左 i,右 j)相遇(i ≥ j)则停止检查;然后基准元素与 检查下标指向的较小元素(即 j 指向的元素)交换(这是因为“基准”元素选在序列首位,而且要求升序排列)。此时,序列将以“基准”元素为间隔,分为 ≤“基准”元素 与 ≥“基准”元素 的两部分,这一过程称为Partition。使用分治算法,重复Partition过程直至序列有序。
以下是 二路双向快排 的实现代码:(上图中 i 为代码中 _lo ,j 为 代码中 _hi)
template<typename T>
void quickSort(T arr[], int lo, int hi)
{
if(hi - lo < 2) return;
//交换序列 中间元素 与 首元素,使中间元素为“基准”元素
T temp = arr[(lo + hi) >> 1];
arr[(lo + hi) >> 1] = arr[lo]
arr[lo] = temp;
int _lo = lo, _hi = hi;
T key = arr[lo];
while(true)
{
while(arr[++_lo] < key && _lo < hi); //向右找≥key的元素
while(arr[--_hi] > key && _hi > lo); //向左找≤key的元素
if(_lo >= _hi) break; //_lo与_hi相交时结束循环
temp = arr[_lo]; arr[_lo] = arr[_hi]; arr[_hi] = temp; //交换_lo,_hi对应的值
}
temp = arr[_hi]; arr[_hi] = key; arr[lo] = temp; //“基准”值与_hi对应值交换
/* 为什么与 _hi 对应的值交换呢?
1、基于升序排列
2、把基准元素放到了前面
条件1,2 决定了 基准元素的位置 应该存放比基准元素 小 或者 等 的元素,
而 _hi 只有遇到 <= 基准元素 的元素时才会停下! 即 _hi 最终指向<= 基准元素 的元素
*/
quickSort(arr, lo, _hi);
quickSort(arr, _hi + 1, hi);
}
快速排序是不稳定的排序方法。另外快排(未优化的快排)在大量重复数据和基本有序数据排序时效率较低。
对于大量数据重复的排序,可以采取三路快排的方式。
对于基本有序数据,则可以随机化基准元素(或者基准取中间,基准三取中)提高排序效率。