(三)几种排序算法的学习总结(快速排序)

4 篇文章 0 订阅

快速排序:“二十世纪最伟大的算法”
核心思想:每次从数组中选择一个元素作为基点,之后把该元素移动到排好序时应该所处的位置,使得基点之前的元素都小于它、之后的元素都大于它。之后对小于它、大于它的子数组分别递归进行快速排序
将元素移动到合适位置的函数:Partition
通常使用第一个元素作为分界的标志点:索引——l,元素值——v
大于v与小于v的分界点索引:j
当前访问的元素索引:i,元素值:e
最终目的,使得:
arr[l+1…j]< v
arr[j+1….i-1]> v
分为两种情况:
1.e > v
2.e < v

再交换l与j位置的元素,使得基点在合适的位置上

//对arr[l...r]部分进行partition操作 
//返回p,使得arr[l...p-1]<arr[p];arr[p+1...r]>arr[p] 
template<typename T>
int __partition(T arr[],int l,int r){
    //优化1:随机选取一个基数,而非最左端的元素
    //swap(arr[rand()%(r-l+1)+l],arr[l]); 
    //基数、标准 
    T v=arr[l];
    //i是当前考虑的位置 
    //arr[l+1...j]<v;arr[j+1...i)>v 
    int j=l;        //使得两个子数组初始都为空 
    for(int i=l+1;i<=r;i++){
        if(arr[i]<v){
            swap(arr[++j],arr[i]);
            //j++;
        }
    } 
    swap(arr[l],arr[j]);
    return j;
} 

template<typename T>
void __quickSort(T arr[],int l,int r){
    //索引非法处理
    if(l>=r)
        return;
    int p=__partition(arr,l,r);
    __quickSort(arr,l,p-1);
    __quickSort(arr,p+1,r);
}

template<typename T>
void quickSort(T arr[],int n){
    //优化1:设置随机种子 
    //srand(time(NULL));
    __quickSort(arr,0,n-1);
}

缺点1:对于近乎有序的数组排序效率低下,运行过程中形成的递归树平衡度较差,对于完全有序的数组,算法复杂度直接退化为O(n*n)。
优化1:随机选取排序的基准元素
缺点2:对于有大量重复键值的数组排序效率低下,同样可能造成算法复杂度退化为O(n^2)。
优化2:

//双路快速排序:
template<typename T> 
int __partition2(T arr[],int l,int r){
    swap(arr[rand()%(r-l+1)+l],arr[l]);     
    T v=arr[l];
    //arr[l+1...i]<=v;arr[j...r]>=v 
    int i=l+1,j=r;
    while(true){
        while(i<=r&&arr[i]<v)i++;
        while(j>=l+1&&arr[j]>v)j--;
        if(i>j)break;
        swap(arr[i],arr[j]);
        //索引移动到下一个要考察的位置
        i++;
        j--; 
    }
    //循环结束时三个索引:
    //i: 从前向后看第一个大于等于v的位置 
    //j: 从后向前看第一个(数组中最后一个)小于等于v的元素位置
    //v: 小于等于v一端 
    swap(arr[l],arr[j]);
    return j; 
}

优化3:考虑到可能会有很多“等于v”的键值,还可以进一步优化出三路快速排序。增设一个存放“等于v”元素的子数组,直接省去这部分递归
索引含义如下:
lt——指向“小于v”部分的最后一个元素,使得arr[l+1…lt] < v

gt——指向已经处理过的后面的第一个“大于v”部分的元素,使得arr[gt…r] > v

则arr[lt+1…i-1]==v
分情况讨论索引为i的元素:
1. e == v:该元素直接纳入“等于v”部分,i++
2. e < v :将该位置元素和“等于v”部分的第一个元素交换,lt++
3. e > v :将该位置元素直接与“gt-1”位置元素进行交换,gt–,i索引无需进行维护,依然指向了一个没有经过处理的元素

整个处理过程结束后,lt、gt分别指向了“小于v”部分和“大于v”部分的最后一个(第一个)元素,i与gt重合,此时交换l位置元素与lt位置元素即可

最后结果:
“小于v”部分:arr[l…lt-1] < v;
“等于v”部分:arr[lt…gt-1] ==v;
“大于v”部分:arr[gt…r] > v;

三路快速排序实现:

//三路快速排序处理:arr[l...r]
//将arr[l...r]分为<v,==v,>v三个部分 
//之后递归对<v,>v 两部分继续进行三路快速排序 
template<typename T>
void __quickSort3Ways(T arr[],int l,int r){
    //递归结束判断
    if(l>=r)
        return;
    //partition
    swap(arr[rand()%(r-l+1)+l],arr[l]);
    T v=arr[l];
    //注意初始值的设定,必须满足三个子数组开始都为空 
    int lt=l; //arr[l+1...lt]<v 
    int gt=r+1; //arr[gt...r]>v 
    int i=l+1; //arr[lt+1...i)==v 
    while(i<gt){
        if(arr[i]<v){
            swap(arr[i],arr[lt+1]);
            lt++;
            i++;
        }
        else if(arr[i]>v){
            swap(arr[i],arr[gt-1]);
            gt--;
            //i索引无需处理,依然指向了一个未经过处理的元素 
        }
        else{//arr[i]==v 
            i++;
        }
    }
    swap(arr[l],arr[lt]);

    __quickSort3Ways(arr,l,lt-1);
    __quickSort3Ways(arr,gt,r);
}

template<typename T>
void quickSort3Ways(T arr[],int n){
    srand(time(NULL));
    __quickSort3Ways(arr,0,n-1);
}

对于快速排序就介绍到这里了,更基础的选择、插入排序详见前两篇文章:http://blog.csdn.net/draper__qyt/article/details/77980407
http://blog.csdn.net/draper__qyt/article/details/77984775
注:本文的内容来自慕课网的课程《c++,java算法与数据结构》,主讲人为刘宇波老师~极力推荐!
这里附上课程链接:http://coding.imooc.com/class/71.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值