前言
快排最主要的就是选定一个基准值key,作为比较的标准,一次快排结束后key一定
会在序列的正确位置,在我们把key放到正确位置的过程中,其他元素也变得相对以前要更加有序,按照这个思路我们只需要多次递归使用快排就行了。
下面为找基准值key的方法,我们采用找中间元素:
int GetMidNumi(int* a, int left, int right) {
int mid = (right + left) / 2;//
//中间的元素的下标
if (a[left] < a[mid]) {
if (a[mid] < a[right]) {
return mid;
}
else if (a[left] > a[right]) {
return left;
}
else {
return right;
}
}
else {
if (a[mid] > a[right]) {
return mid;
}
else if (a[left] < a[right]) {
return left;
}
else {
return right;
}
}
//最终返回left,right,mid为下标的元素中
//中间值的下标
}
一、递归方法(常用)
1.hoare方法:
记住一点:左边做key,右边先走
可以用插入排序来优化,当然也可以不用,全用快排
插入排序
这个链接里面有我插入排序的内容
void QuickSort1(int* a, int left, int right) {
if (left >= right) {
return;
//退出此次函数调用
}
if (right - left + 1 > 10) {
//记住一点:左边做key,右边先走
int begin = left;
int end=right;
//保存左右边界值
int midi = GetMidNumi(a, left, right);
//获取中间元素下标
if (midi != left) {
Swap(&a[midi], &a[left]);
}
//把中间位置的数据与最左边进行交换
int keyi = left;
//存储基准值的下标
while (left < right) {
while (left < right && a[right]>=a[keyi]) {
right--;
//右边找小
}
while (left < right && a[left] <= a[keyi]) {
left++;
//左边找大
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
//此时left==right
//left此时的位置就是key的正确位置,让其进行交换
keyi = left;
//以keyi的下标作为分界点,因为其已经在正确位置了
//不需要在进行递归了
//【begin,keyi-1】keyi【keyi+1,end】
QuickSort1(a, begin, keyi - 1);
QuickSort1(a, keyi + 1, end);
}
else {
InsertSort(a + left, (right - left + 1));
//对快排进行优化,数据少的时候就用插入
//当然你也可以if (right - left + 1 > 10)不要
//也不用插入,全进行快排
}
}
2.挖坑法:
void QuickSort2(int* a, int left, int right) {
if (left >= right) {
return;
}
if ((right - left + 1) > 10) {
int begin = left;
int end = right;
int midi = GetMidNumi(a, left, right);
if (midi != left) {
Swap(&a[midi], &a[left]);
}
int key = a[left];
//保存基准的值
int hole = left;
//让最左边为坑
while (left < right) {
while (left < right && a[right] >= key) {
right--;
//右边找小
}
a[hole] = a[right];
//把其放在坑位
hole = right;
//坑位更新,此时右边为坑
while (left < right && a[left] <= key) {
left++;
//左边找大
}
a[hole] = a[left];
hole = left;
//同上
}
a[hole] = key;
//最后hole就是left==right的位置
//也就是key的正确位置
//【begin,hole-1】hole【hole+1,end】
QuickSort1(a, begin, hole - 1);
QuickSort1(a, hole + 1, end);
}
else {
InsertSort(a + left, (right - left + 1));
}
}
3.前后指针法:
void QuickSort3(int* a, int left, int right) {
if (left >= right) {
return;
}
if ((right - left) > 10) {
int begin = left;
int end = right;
int midi = GetMidNumi(a, left, right);
if (midi != left) {
Swap(&a[midi], &a[left]);
}
int keyi = left;
int prev = left;
//cur找到比a【keyi】小的值,++prev,cur与prev位置交换
//cur++
//cur找到比a【keyi】大的值,cur++
//也就是无论怎样,cur一定++;
int cur = prev + 1;
while (cur <= right) {
//把比a【keyi】小的值往左翻
//prev要么紧跟着cur
//要么prev与cur之间间隔着一段比a【keyi】
//大的区间
if (a[cur] < a[keyi] && ++prev != cur) {
Swap(&a[cur], &a[prev]);
}
cur++;
}
Swap(&a[prev], &a[keyi]);
//prev为a【keyi】的正确位置
keyi = prev;
//【begin,keyi-1】keyi【keyi+1,end】
QuickSort2(a, begin, keyi - 1);
QuickSort2(a, keyi + 1, end);
}
else {
InsertSort(a + left, (right - left + 1));
}
}
二、非递归
非递归其实就是快排的思想分割他们的区间只不过用的是循环的方式
所以要用到栈来控制区间
栈的基本操作
不明白栈的可以先看看这篇
int PartSort(int* a, int left, int right) {
int midi = GetMidNumi(a, left, right);
if (midi != left) {
Swap(&a[midi], &a[left]);
}
int prev = left;
int cur = prev + 1;
int keyi = left;
while (cur<= right) {
if (a[cur] < a[keyi] && ++prev != cur) {
Swap(&a[cur], &a[prev]);
}
cur++;
}
Swap(&a[prev], &a[keyi]);
keyi = prev;
//这个其实就是快排的一部分,找到key的正确位置返回
return keyi;
}
void QuickSortNonR(int* a, int left, int right) {
ST st;
STInit(&st);
STPush(&st, right);
//栈先进先出,
//所以【0,9】
//先入右边的【9,0】
//0先出
STPush(&st, left);
while (!STEmpty(&st)) {
int begin = STTop(&st);
STPop(&st);
int end = STTop(&st);
STPop(&st);
int keyi = PartSort(a, begin, end);
if (keyi + 1 < end) {
STPush(&st, end);
STPush(&st, keyi + 1);
}
if (begin < keyi - 1) {
STPush(&st, keyi - 1);
STPush(&st, begin);
}
}
STDestroy(&st);
}