目录
前言
快速排序是一种效率非常高的排序方法,排序时需要先取一个基准值。该方法有三种不同实现方式。
快速排序hoare版
先取一个基准值,一般为了使方法更加简化,我们会取最左边或最右边的元素为基准值,,然后标记前后元素位置,分别比较,(如果是升序)将前边比基准值大的元素与后面比基准值小的元素交换,直到前后标记相遇,第一趟循环结束,循环结束后将两标记相遇处的元素与基准值交换。
流程:先确定基准值
left移动到比基准值大的值上,right移动到比基准值小的元素上
然后left与right交换
left与right继续移动寻找对应元素
left与right相遇时第一趟循环结束
交换相遇处元素与基准值,可以发现原来的基准值3 左边的元素全都是小于3的,右边全是大于3的
然后left不变,将第一趟相遇处作为第二趟的right,继续重复以上操作
代码实现
int PartSort1(int* a, int left, int right){ //hoare版
int key = left; //取最左为基准值
while (left < right){
while (left<right && a[right] >= a[key]){ //如果是升序且key是left,则right一定要在left前
right--;
}
while (left<right && a[left] <= a[key]){//如果是降序且key是left,则该循环在前
left++;
}
if (left < right){
Swap(&a[left], &a[right]);
}
}
Swap(&a[left], &a[key]);
return left;
}
void QuickSort(int* a, int left, int right){ //快速排序
if (left >= right)
return;
int key = PartSort1(a, left, right);
QuickSort(a, left, key - 1); //递归
QuickSort(a, key + 1, right);
}
挖坑法
该方法思路相比以上方法有一点点变化,排序时同样是先找最左或最右为基准值,然后标记前后位置,但是找到左边(或右边)大于(或小于)基准值的元素用于覆盖最右边(左边)(基准值的位置),最后把基准值填在左右标记相遇处。
left找到比基准值大的元素
用left覆盖right位置
right向前移动找比基准值小的元素,如果left与right不相等则将right覆盖left,直到与left相遇退出
最后将基准值覆盖在相遇处
第一趟完成后,基准值左边都是小于自身的,右边都大于自身。然后重复以上操作
代码实现
int PartSort2(int* a, int left, int right){ //挖坑法
int key = a[right]; //取最右为基准值
while (left < right){
while (left<right && a[left] <= key){
left++;
}
a[right] = a[left];
while (left < right && a[right] >= key){
right--;
}
if (left < right)
a[left] = a[right];
}
a[left] = key;
return left;
}
void QuickSort(int* a, int left, int right){ //快速排序
if (left >= right)
return;
int key = PartSort2(a, left, right);
QuickSort(a, left, key - 1);
QuickSort(a, key + 1, right);
}
前后指针法
与之前两种方法又有了一点点的区别,排序时设置基准值key和两个指针,一前(cur)一后(prev),只要cur上的元素小于基准值,且prev的下一位元素不是cur,则交换cur与prev,直到cur大于end(最右边)结束循环,然后交换prev与key的值。
取最右边为基准值
先判断cur是否小于基准值,如果不小于,则后移,prev不动,如果小于则判断prev的下一位是否与cur相同,相同则cur后移。 这里3是小于4的,所以先++prev后prev与cur相同,cur也后移一位
再判断此时cur小于基准值了,先++prev是等于cur 的,cur后移。
再判断,cur是5不小于基准值,cur后移,prev不动
此时cur小于基准值,先++prev(此时prev是5)也不等于cur(此时是2),则交换cur与prev后cur后移
重复以上判断
cur与end相遇,退出循环,再交换prev的下一位与基准值的值
最后再重复以上所有操作,可以看到第一趟结束后基准值4前面都是小于4的,后面元素都大于4
代码实现
int PartSort3(int* a, int left, int right){ //前后指针法
int key = right;
int prev = left-1,cur=left;
//上面写int prev = left,cur=left+1;,下面写Swap(&a[prev], &a[key]);会出问题
while (cur < right){
if (a[cur] <= a[key] && ++prev != cur){
Swap(&a[cur], &a[prev]);
}
cur++;
}
Swap(&a[++prev], &a[key]); //需要先++prev再交换
return prev;
/* 基准值在前也行,采用降序
int key = left;//选取第一个元素为基准值
int prev = left;//前指针
int cur = prev + 1;//后指针
whiel(cur <= end)
{
if(array[cur] > a[key] && ++prev != cur)//如果cur的值小于key判断++prev的值是否等于cur
//若不等于,则交换prev和cur的值
swap(&a[cur],&a[prev]);
cur++;//cur向后移动
}
//当跳出循环时,说明prev及之前的值都是小于基准值的数,则交换prev指向的值和基准值
swap(&a[++prev], &a[key]);
return prev;//返回此时基准值的下标,便于下次递归调用时分组
*/
}
void QuickSort(int* a, int left, int right){ //快速排序
if (left >= right)
return;
int key = PartSort3(a, left, right);
QuickSort(a, left, key - 1);
QuickSort(a, key + 1, right);
}
优化思路
可以使用三数取中法(即比较最左,最右,和中间三个数的大小,选择三个数中数值为中间的那个作为基准值),这样可以提高些许效率。