选择数组中的一个元素pivot,该元素作为基准
注意:将小于基准的元素移到左边,大于基准的元素移到右边(分区操作)
数组被pivot分为两部分,继续对剩下的两部分做同样的处理
直到所有子集元素不再需要进行上述步骤
一.单向指针扫描
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//分治
int partition(int ar[], int p, int r) {
int big = r, scan = p + 1;//左右指针
int pivot = ar[p];//目标元
//最终big下标<scan下标
while (scan<=big) {
//数组可能越界,于是下方加入big > scan
if(scan<=big && ar[scan] > pivot) {
swap(&ar[scan], &ar[big]);//如果scan扫描到比大于pivot,就scan就与big交换,最终big那部分元素一定要比scan部分大
big--;
}
else {
scan++;
}
}
swap(&pivot, &ar[big]);//最后再将目标元与big指向的值交换
return big;//返回big值
}
//快排进行
void quicksort(int ar[], int p, int r) {
//当pivot>=区域右边界时终止
if (p < r) {
int q = partition(ar, p, r);//q=big
//下方进行递归
quicksort(ar, p, q - 1);//左部分
quicksort(ar, q + 1, r);//右部分
}
}
![](https://img-blog.csdnimg.cn/img_convert/48f56ecd52462c7cac22320016158bf8.png)
如上图,最终b下标小于a下标示数。
二.双向指针扫描
void swap(int* a, int* b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
//分治
int partition(int ar[], int p, int r) {
//左右双指针
int left = p + 1, right = r;
int pivot = ar[p];
//下标
while (left <= right) {
//left一直向右走,扫描到大于pivot停止
while (left <= right&&ar[left] <= pivot) {
left++;
}
//right一直向左走,扫描到小于pivot停止
while (left <= right&&ar[right] >= right) {
right--;
}
//上面2个while循环退出的时候,即遇到大于情况,进行交换
if (left < right) {
swap(&ar[left], &ar[right]);
}
}
swap(&pivot, &ar[right]);//退出时,right指向最后一个小于主元的位置
//这样,right位置就是左右两区域临界处
return right;
}
//快排进行
void quicksort(int ar[], int p, int r) {
//当pivot>=区域右边界时终止
if (p < r) {
int q = partition(ar, p, r);//q=right
//下方进行递归
quicksort(ar, p, q - 1);//左部分
quicksort(ar, q + 1, r);//右部分
}
}
![](https://img-blog.csdnimg.cn/img_convert/f55a9e32507c97188bad3aa793f07bb2.png)
如图,两种临界情况都是b下标<a下标,并且a位于最后一个<处
三.三指针分区
void swap(int* a, int* b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
//分治
int partition(int ar[], int p, int r) {
//三指针
int scan=p+1, equal=scan, big=r;
int pivot = ar[p];
//下标
while (big > scan) {
//scan扫描小于就向右移动
while (ar[scan] <=pivot) {
scan++; equal++;
//当遇到相等时,将scan向右移一位,而equal还在scan的上一位,于是将equal值与scan调转
if (ar[scan] = pivot) {
scan++;
swap(&ar[equal], &ar[scan]);
}
}
//遇到大的时,将big与scan值互换,big向左移
while (ar[scan] > pivot){
swap(&ar[scan], &ar[big]);
big--;
}
}
return big;
}
void quicksort(int ar[], int p, int r) {
//当pivot>=区域右边界时终止
if (p < r) {
int q = partition(ar, p, r);//q=big
//下方进行递归
quicksort(ar, p, q - 1);//左部分
quicksort(ar, q + 1, r);//右部分
}
}
![](https://img-blog.csdnimg.cn/img_convert/f4b441961f29d9f03dd53b216e5447a1.png)
优化pivot的选择
A.三点中值
取p,r,mid三点中的中间值作为主元
int choose(int ar[],int p, int r) {
int mid = p + (r - p) << 1;
int pivot = -1;
if (ar[p] > ar[r] && ar[p] < ar[mid]) {
pivot = p;
}
else if (ar[r] > ar[p] && ar[r] < ar[mid]) {
pivot = r;
}
else pivot = mid;
return pivot;
}
B.绝对中值,O(n)
int chooose(int ar[],int r,int p) {
int size = r + p - 1;//原数组大小
int group = (size % 5==0) ? (size / 5) : (size / 5 + 1);//进行分组
int* help, index = 0;
//利用循环,对每一组进行插入排序,寻找中间值
for (int i = 0; i < group; i++) {
if (i = group - 1) {
//先对最后一组进行插入排序;
help[index++] = //最后一组排序后中间值;
}
else {
//排序其他组;
help[index++]= //排序后中间值;
}
}
//对help数组进行插入排序,选取中间值
return //中间值
}
C.调用插入排序
已知插入排序时间复杂度为O(n^2),在数值个数位于8之前,插入排序要比快排快
于是可以在8之前调用插入排序。
if (r - p + 1 <= 8) {
//调用插入排序
}