浅谈快速排序的各类优化

快速排序的核心是-paration函数。

快排的核心思想:每次选取数组中某一个元素,将小于它的所有元素放在它的左边,大于它的所有元素放在它的右边,然后递归处理左区域和右区域。

最好的情况下,复杂度是O(nlogn),最差的情况下会退化成O(n^2),但是基于概率学可以证明,复杂度的期望是接近O(nlogn)的。

优化从何谈起?

优化1:防止最差情况的发生-也就是防止选取的划分元素是该区域的min或者max,

A.基于rand()的方法,我们可以通过rand()随机生成划分元素的索引。

B.多中取值:对于一个区间,我们可以取它的L,R,(L+R)/2三个位置上的元素,然后取其中位数,这个数一定不可能是边界值-三中取值,另外还有九中取值等,但是注意,并不是越多越好,对于小区间来说,三中取值足以,但是对于九中取值,反而会浪费找中位数的常数时间,优化本末倒置。

优化2:重复元素的处理

经典的快排是一次只将一个数归位,但是对于有大量重复元素的数组来说,这样做无疑会拖慢速度。

我们需要先看一个荷兰国旗问题:

给你一串数和一个x,要求你把小于x的数放左边,等于x的数放中间,大于x的数放右边。

解题思路:基本paration函数加以变化即可,我们设置一个L=l-1表示左部区间的右端点,R=r+1表示右部区间的左端点,一个游标cur=l(初始化为当前区间的左端点)扫描当前区间。

A.如果当前元素A[cur]是小于划分元素的数,将L向右移动1位,并将该位置的元素和cur位置的元素交换,交换后cur++。(解释:我们可以知道cur>L是恒成立的,所以当A[++L]和A[cur]交换之后,A[cur]的元素值是一定满足<=划分元素条件的,无需再判断,直接后移1位)。

B.如果当前元素A[cur]是大于划分元素的数,将R向左移动1位,并将该位置的元素和cur位置的元素交换,交换后cur不动。(解释:当我们将A[--R]和A[cur]交换后,我们并不能确定当前的A[cur]和划分元素的大小关系,所以需要重新判一次)。

C.如果当前元素A[cur]是等于划分元素的数,不用交换,直接cur++。

终止条件:cur==R,这说明已经向右扫到了划分好的元素,所以没必要继续扫了。

#include<cstdio>
#include<algorithm>
using namespace std;
int n,A[100]; 
void paration(int l,int r,int x){
    int L=l-1,R=r+1,cur=l,t;
    while(cur!=R){
        if(A[cur]<x){
            swap(A[++L],A[cur++]);
        }
        else if(A[cur]>x){
            swap(A[--R],A[cur]);
        }
        else cur++;
    }
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&A[i]);
    }
    paration(1,n,3);
    for(int i=1;i<=n;i++){
        printf("%d ",A[i]);
    }
    return 0;
} 

改进的paration函数较朴素的paration函数有一个常数级优化,在重复元素较多的情况下可以迅速缩小递归处理的区间。上述代码的paration函数后,我们会得到一个L,R,将他们返回作为下一次的递归区间。

下面的代码采用的是随机选取划分元素以及加速paration函数的优化。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
using namespace std;
int n,A[100];
struct  returnType{
    int left,right;
};  

returnType paration(int l,int r,int x){
    int L=l-1,R=r+1,cur=l,t;
    returnType val;
    while(cur!=R){
        if(A[cur]<x){
            swap(A[++L],A[cur++]);
        }
        else if(A[cur]>x){
            swap(A[--R],A[cur]);
        }
        else cur++;
    }
    val.left=L,val.right=R;
    return val;
}

void quicksort(int l,int r){
    if(l>r||(r-l)<=1)return;
    srand((unsigned) time(NULL));
    int index=rand()%n+1;
    returnType val=paration(l,r,A[index]);
    quicksort(l,val.left);
    quicksort(val.right,r);
}


int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&A[i]);
    }
    quicksort(1,n);
    for(int i=1;i<=n;i++){
        printf("%d ",A[i]);
    }
    return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值