快速排序(递归方法)
基本思想
先找到一个合适的数,作为支点pivot并放在段末尾,然后两个指针在数据段的首位,开始遍历判断,目的是为了让下标为L的位置,左侧均小于支点值(支点本身不小于支点),右侧则大于支点值(不包括数据段末尾的支点),然后L和支点位置互换。代码如下,注解很详细:
// 找到中位数 数组长度要大于3,要求三个数不相同
template<class T>
int findMed(T a[], int &i ,int &j)
{
if((j - i ) < 3)
return i;
else{ // 中位数一定是 大于一个,小于另外一个
int k = i+(j-1)/2;
if ((a[i]-a[k]) * (a[i]-a[j]) < 0)
return i;
else if ((a[k]-a[i]) * (a[k]-a[j]) < 0)
return k;
else if ((a[j]-a[k]) * (a[j]-a[i]) < 0)
return j;
else if // 这一步感觉没啥用,因为前提条件说了三数不能相等,但是不加,编译器又会警告:control reaches end of non-void function [-Wreturn-type]
return i;
}
}
// 交换函数
template<class T>
void swap(T a[], int i ,int j)
{
T tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
template<class T>
void quickSort(T a[], int leftEnd, int rightEnd)
{ // 对 a[leftEnd, rightEnd] 进行排序,且a[rightEnd + 1] > a[leftEnd, rightEnd]
if( leftEnd >= rightEnd)
return;
//找一个合适的支点
int median = findMed(a, leftEnd, rightEnd); // 尽可能找数值为中间的数
swap(a, median, rightEnd); // 把合适的数作为支点,放在段的末尾
T pivot = a[rightEnd]; // 支点是在末尾
int L = leftEnd;
int R = leftEnd;
// (如6 3 8 4 8,递归一次后,变成6 3 4 8 8,如果< 改为 <=,变为6 3 8 4 8)
while (R < rightEnd) //rightEnd 是n-2,也就是数组的倒数第二个元素
{// 每循环一次,先做if判断,再递增1,L只有当符合条件交换后,才递增1;
// 这样能够保证L左边放的数据,都是小于支点数据的
if (a[R]<pivot)
{
// 排序操作,
swap(a, L, R);
++L;
}
++R;
}
//放支点,把支点放在下标为L的位置,L左边的肯定都是小于支点的数,L所指的数据,分三种情况:
// 极端1:支点左边的都小于支点,那么最后,L=R=rightEnd=n-1,然后自己和自己交换,顺序不变;
// 极端2:支点左边的都大于支点,那么最后,L=leftEnd, R=rightEnd=n-1,支点放在和段手数据交换,符合条件
// 一般情况: L所指的数大于pivot,R=n-1,放支点,这样支点左边的小,右边的小;
swap(a, L, rightEnd);
// 递归调用
quickSort(a, leftEnd, L-1);
quickSort(a, L+1, rightEnd);
}
template<class T>
void quickSort(T a[], int n)
{ // 对 a[0 : n-1] 快速排序
if (n <= 1) return;
quickSort(a, 0 , n-1);
}
测试主函数:
// 测试
#include<iostream>
int main() {
using namespace std;
int ms[] = {6, 11, 3, 5, 2, 8};
cout<< "原数列:";
int size = 6;
for(int i = 0;i<size;i++)
{
cout<< ms[i] << " ";
}
cout<< "\n" << "归并排序后:";
quickSort(ms, size);
for(int i = 0;i<size;i++)
{
cout<< ms[i] << " ";
}
return 0;
}