第一种,挖坑法:
顾名思义,挖坑法,首先要有一个"坑",现在我们将第一个数字取出放到key中,现在6这个位置就是一个可以被覆盖的位置,也就是一个坑位,设开始位置为begin,末尾位置为end,现在我们从end找比key小的数字,也就是2的这个位置开始找,2比6小.那么就将2移动到6的位置上,变为:
这时的坑位被2填补上了,那么新的坑位就是原来2的位置,现在我们从begin再找比key大的数字,找到了8,将8放到坑位 变为:
以此类推,再从后面找比key小的,然后放到坑位,原来的位置变成新的坑位, 一直找到end与begin相遇,或者begin>end,为什么是begin>=end时就不找呢,begin是是找比key大的,当他遇到end说明前面都是比key小的,这时我们的目的就已经达成了,end同理
,
那么最后再将key,也就是6放到坑位当中,就会发现,前面的数都比6小,后面的数都比6大,这就代表6这个数已经在属于他排好后的位置上了,那么6这个数字就被排好了
那么既然6的左边都比6小,6的右边都比6大,那么是不是就代表着,左边排好,右边也排好,那么整个排序排好了? 是的,那我们就应该像6那样,把每一个数字都排到属于他的位置上,因为6已经排好,所以这时的区间要有所改变,从2 到1 是一个区间,从7 到 8是一个区间,这时左边区间的begin和end分别为2的位置和1的位置了,我们先将2放到属于他的位置上,结果为
那么我们将这个区间再进行划分,分为2的左区间和右区间,左区间的只有一个1,说明他就已经是有序的了,那我们就不再管他,以同样的方式去排右区间,最终6的左边就排好了,那么右边也以同样的方法实现即可,实现的方法一种是循环实现,一种是递归实现,这里我们先采用递归实现
int Getmid(int* arr, int left, int right)//三数取中法
{
int mid = (left + right) >> 1;
if (arr[left] < arr[mid])
{
if (arr[mid] < arr[right])
return mid;
else if (arr[left] > arr[right])
return left;
else
return right;
}
else
{
// arr[left] > arr[mid]
if (arr[left] < arr[right])
return left;
else if (arr[mid] > arr[right])
return mid;
else
return right;
}
}
void Swap(int* a1, int* a2)
{
int temp = *a1;
*a1 = *a2;
*a2 = temp;
}
int Quick_Pit_Sort(int* arr, int left, int right)
{
//当数组接近有序的时候,如果再默认坑为开始第一位数的话效率就会很低,三数取中法就
//可以解决这种问题,顾名思义,就是在区间内去掉最大数和最小数,有效的提高了接近有序时的效率
int Index = Getmid(arr,left,right);
Swap(&arr[left],&arr[Index]);
int begin = left;
int end = right;
int pit = begin;
int key = arr[begin];
while (begin < end)
{
while (begin < end && arr[end] >= key)
--end;
arr[pit] = arr[end];
pit = end;
while (begin < end && arr[begin] <= key)
++begin;
arr[pit] = arr[begin];
pit = begin;
}
arr[pit] = key;
}
void Quick_Sort(int* arr, int left, int right)
{
if (left >= right)
return;
int keyIndex = Quick_Pit_Sort(arr, left, right);
Quick_Sort(arr,left,keyIndex-1);
Quick_Sort(arr,keyIndex+1,right);
}
这里还需要说一下三数取中法,由于当需要排序的内容接近或者已经有序的时候,这时取到的都是最大值或者最小值,挖坑法的坑位就会一遍遍的遍历,不发生交换或者很少发生交换,时间复杂度就会达到O(N^2),这种情况下使用三数取中法即可避免取到最大值或者最小值