快速排序是使用最多的排序方式,其复杂度为1.39NlogN,略高于归并排序,但是由于其移动次数少且不需要,所以一般情况下快速排序会更快。
快速排序原理是将数组第一个元素作为切分元素,比其小的元素排在其左侧,比其大的元素排在其右侧,然后再分别对其两侧的子数组进行切分,最终得到排好序的数组。
三项切分的快速排序则是在快速排序的基础上将与切分元素相等的所有元素找到并与切分元素一起排在中间,然后只对小于切分元素的左侧子数组和大于切分元素的右侧子数组进行再次切分。三项切分能将复杂度减小到N到1.39NlogN之间,从而提高快速排序方法在处理有大量重复数据的数组时的速度。
图示和代码分别如下:
void _swap(int t[], int *lo, int *hi) //交换两元素
{
int temp = *lo;
*lo = *hi;
*hi = temp;
}
int *_partition(int t[], int *lo, int *hi) //切分函数
{
const int cmp = *lo; //记录切分元素
int *left = lo; //左半部分指针
int *right = hi+1; //右半部分指针
while(true)
{
while(*(++left) < cmp) if(left >= hi) break; //左指针右移
while(*(--right) > cmp) if(right <= lo) break; //右指针左移
if(left >= right) break; //退出循环条件
_swap(t, left, right); //左指针指向大于切分元素的数,右指针指向小于切分元素的数,交换二者
}
_swap(t, lo, right); //交换切分元素与右指针指向的数(此时右指针在左指针之前)
return right; //返回右指针指向的位置(此时该位置处的值为切分元素)
}
void Quick(int t[], int *lo, int *hi) //标准快排
{
if(lo >= hi) return; //结束递归
int *j = _partition(t, lo, hi);
Quick(t, lo, j-1); //将切分元素前后两部分继续递归切分
Quick(t, j+1, hi);
}
void Quick3way(int t[], int *lo, int *hi) //三项切分的快排
{
if(lo >= hi) return; //结束递归
int cmp = *lo;
int *left = lo;
int *right = hi;
int *i = lo+1;
while(i <= right) //i > right表示已遍历数组
{
if(*i < cmp) _swap(t, left++, i++); //i指向元素小于切分元素则交换i和left指向的元素后二者自加1
else if(*i > cmp) _swap(t, right--, i); //i指向元素大于切分元素则交换i和right指向元素,right自减,i不变
else i++; //i指向元素等于切分元素,则i自加
}
Quick3way(t, lo, left-1); //递归调用
Quick3way(t, right+1, hi);
}