快速排序详解——多种实现方式

14 篇文章 0 订阅
14 篇文章 0 订阅

快速排序

快速排序是一种交换排序,是基于二叉树结构的交换排序方法,基本思想如下:

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

将区间按照基准值划分为左右两半部分的常见方式为:

hoare版本实现快速排序

hoare版本实现快排的方式为:如果假定key基准值为最左边,然后应该从右边开始向左查看,遇到大于基准值key的right—,反之停在这个位置,再让左手left开始找大于key的元素,遇到之后也停止,最后两者交换,完成这一趟循环之后(left和right两者相遇)key是大于相遇位置的元素的,最后交换着这两个元素,将相遇位置下表赋值给key,再递归

代码实现:

//抽离Swap交换函数
void Swap(int* a,int* b)
{
    int tmp=*a;
    *a=*b;
    *b=tmp;
}
    
void QuickSort(int a[], int left, int right)
{
    if (left >= right) return;

    int begin = left;
    int end = right;
    int keyi = left;//初始化keyi基准值的下标

    while (left < right)
    {
        //以左手为基准值,那么就要从右手开始
        while (left<right && a[right]>a[keyi]) {
            --right;
        }
        while (left < right && a[left] < a[keyi])
        {
            ++left;
        }
        //然后找到位置之后,进行交换、
        Swap(&a[left], &a[right]);
    }
    //整个一趟交换完毕之后,进行交换key和left的位置
    Swap(&a[keyi], &a[left]);
    //left 和right 的运动的,begin和end都是为了保留原来的left和right,为了最后的递归
    keyi = left;

    QuickSort(a, begin, keyi - 1);
    QuickSort(a, keyi + 1, end);
}

这是第一个版本,我们比较好理解,这个本质上就是二叉树的结构演化出来的快速排序,所以,我们知道一点,当key保持在最中间位置的时候,算法是最优的

如果说对于有序数组,实际上key基准值再从最左边或者最右边开始,会导致key永远在最左边或者右边,实际上是和冒泡排序差不多,所以这个时候,就是最坏的情况,时间复杂度为O(n2)

上述方法的快排竟然对于有序的情况是最坏的情况,所以我们需要对于这个改正,改变的是key基准值的下标的位置,所以下面两个方法可以对于上述快排进行优化。

优化快排算法方式

有两种方式,第一种是获取随机keyi下标,另一种是三数取中方法

第一种随机值法

//使用rand()函数
void getKeys(int* a,int left,int right)
{
    int randi=left+rand()%(right-left);
    Swap(&a[left],a[randi]);
}
//剩下的还是那些
将keyi=left  然后while循环即可,下面不变

这一种方法是比较好写的方法,比较简单方便

第二种三数取中方法

三数是指left、right、mid,取得不是最大不是最小,恰好处于最中的为元素下标

//这个方法返回的是一个下标,取得keyi的数值之后,还是需要跟keyi交换
int getmidinum(int a[],int left,int right)
{
   	int mid=(left+right)/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;
        }
    }
}
//然后返回的数值赋值给midi
int midi=getmidinum(......);
if(midi!=left)  //判别是否是同一个位置,实际上无所谓这个
Swap(&a[left],&a[midi]);

挖坑法实现快排

挖坑法,实际上和hoare版本差不多的,只是多一个坑的概念

挖坑法,先设定key基准值,形成一个坑位(设定一个变量hole表示坑)然后从右开始找到小于key的数值,然后将这个数值放在坑里,然后赋值hole这个位置,表示新的坑,再左边开始找到大于key的位置,再次放在hole里,继而hole更新为这个左边的位置

类似于上述的过程

void QuickSort1(int a[],int left ,int right)
{
    if(left>=right) return;
    int begin=left,end=right;
    getKeys(a,left,right);
	int hole =left;
    int keyi = left;//初始化keyi基准值的下标
    while (left < right)
    {
        //以左手为基准值,那么就要从右手开始
        while (left<right && a[right]>=a[keyi]) {
            --right;
        }
        a[hole]=a[right];
        hole=right;
        while (left < right && a[left] <= a[keyi])
        {
            ++left;
        }
        a[hole]=a[left];
        hole=left;
       
    }
    //整个一趟交换完毕之后,进行交换key和left的位置
   	a[hole]=a[keyi];
    QuickSort2(a, begin, hole - 1);
	QuickSort2(a, hole + 1, end);
}

快慢指针实现快排

使用两个指针cur和pre来实现快速排序

void QuickSort(int a[],int left,int right)
{
    if(left>=right) return;
 	int midi=getmininum(a,left,right);
    Swap(&a[left],&a[midi]);
    
    int keyi=left;
    int prev=left;
    int cur=left+1;
    
    while(cur<=right)
    {
        if(a[cur]>a[keyi]&&++prev!=cur){
            Swap(&a[cur],&a[prev]);
        }
        ++cur;
    }
    Swap(&a[prev],&a[keyi]);
    keyi=prev;
    
    QuickSort(a,left,keyi-1);
    QuickSort(a,keyi+1,right);    
}

Acwing提供的快排方法

快速排序的优化,对于快速排序的优化还有一种,因为快速排序的基于二叉树结构的,所以在最后一层或者是倒数几层递归次数很多,如果说对于这样的层数进行优化,使得非递归解决排序,会提高不少效率

void QuickSort(int a[],int left,int right)
{
    if(left>=right) return;
    
    int key=a[left];
    int i=left-1;
    int j=right+1;
    while(i<j)
    {
        do{
            ++i;
        }while(a[i]<key);
        do{
            --j;
        }while(a[j]>key);
        if(i<j){
            Swap(&a[i],&a[j]);
        }
    }
    QuickSort(a,l,j);
    QuickSort(a,j+1,r);
}
  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

byg_qlh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值