算法设计学习日志———关于快速排序

今天的内容是快速排序,可以说是为了补上次博客的坑吧

  1. 问题描述
  2. 思考分析
  3. 实现解决
  4. 源代码
  5. 改进与总结




1.问题描述



  排序,一直是一种比较热门的算法话题,有人已经证明了经典排序的时间复杂度下限为o(nlogn),越是好的算法越是接进这个时间复杂度的。
  对一个长度为n的无序数组,对其进行排序,这就是我们今天要解决的问题
  这一次我要写的是——快速排序,它的平均时间复杂度是o(nlogn)。


2.思考分析



  按照我们老师的说法,他认为排序算法可以分成两大类,一类是渐进排序:就是一次无法得到某个元素的正确位置,但通过多次的计算,会逐渐趋向于有序,直到成功,比如合并排序,希尔排序等。另一类是定位排序:就是能一下子确定某个元素的正确位置,如冒泡排序,快速排序等。
  快速排序就是一种定位型排序方式,它是从冒泡排序的基础上演变过来的。
   快速排序算法基本思想是:对输入的子数组a[p:r],按以下三个步骤进行排序。
(1)以a[p]为基准元素将a[p:r]划分成3段a[p:q-1],a[q]和a[q+1:r],使得a[p:q-1]中任何一个元素小于等于a[q],而a[q+1:r]中任何一个元素大于等于a[q]。下标q在划分过程中确定。
(2) 通过递归调用快速排序算法分别对a[p:q-1]和a[q+1:r]进行排序。
(3) 由于对a[p:q-1]和a[q+1:r]的排序是就地进行,所以在a[p:q-1]和a[q+1:r]都已排好的序后,不需要执行任何计算,a[p:r]就已排好序。

template <class Type>
void quickSort(Type *a,int p,int r){

    if(p>=r)return;
    int q=Partition(a,p,r);//返回划分过后,基准元素的位置
    quickSort(a,p,q-1);//左边排序
    quickSort(a,q+1,r);//右边排序
    return;
}

从理论上看此算法很清晰,且一目了然。
那么具体实现怎么样呢


3.实现解决



很明显问题的关键是在算法的第一步,如何实现“将比a[p]小的放在左边,比a[p]大的放在右边”这个功能呢。
这是快速排序算法的最巧妙的地方之一。

int Partition (Type a[], int p, int r)
{
        int i = p, j = r + 1; 
        Type x=a[p];
        while (true) {
            while (a[++i] <x && i<r);//从左边选择一个大于等于x的数
            while (a[--j] >x);//从右边选取一个小于等于x的数
            if (i >= j) break;
             swap(a[i], a[j]);//交换两者
           }
       a[p] = a[j];
       a[j] = x;//将a[p]与a[j]交换。
           return j;//返回划分好后基准元素的位置。
}

Partition,这个函数它会以a[p]为基准元素对a[p:r]进行划分,小于a[p]的放左边,大于a[p]的放右边,并且返回划分好后a[p]这个元素所在的位置,方便之后的递归调用。
注:这个算法的每一句都很巧妙,非常适合研究一下编程的技巧。在此就不详细赘述,以后要多多体会。


4.源代码



现在,按照原先说好的步骤编写源码

#include<iostream>
using namespace std;
template<class Type>
int Partition (Type a[], int p, int r)
{
        int i = p, j = r + 1; 
        Type x=a[p];
        while (true) {
            while (a[++i] <x && i<r);//从左边选择一个大于等于x的数
            while (a[--j] >x);//从右边选取一个小于等于x的数
            if (i >= j) break;
             swap(a[i], a[j]);//交换两者
           }
       a[p] = a[j];
       a[j] = x;//将a[p]与a[j]交换。
           return j;//返回划分好后基准元素的位置。
}

template <class Type>
void quickSort(Type *a,int p,int r){

    if(p>=r)return;
    int q=Partition(a,p,r);
    quickSort(a,p,q-1);
    quickSort(a,q+1,r);
    return;
}
int main()
{
    int a[10]={
    10,3,2,6,4,8,5,9,1,7    
    };

    quickSort(a,0,9);

    for(int i=0;i<10;i++)
    cout<<a[i]<<' ';
    return 0;
}

亲测可用。。


5.改进与分析



这个算法的时间复杂度为o(nlogn),完全契合经典排序算法的下限,可以说是最优的排序算法之一了。
快速排序的运行时间与划分是否对称有关,其最坏情况发生在划分过程产生的两个区域分别包含n-1个元素和1个元素的时候。

T(n) = T(n-1)+0(n) n>1;T(n) = 0(1) n<=1;

T(n)=O(n2) ;
最好情况下,每次划分所取的基准都恰好为中值,即每次划分都产生两个大小为n/2的区域。
T(n) = O(1) n<=1; T(n) = 2T(n/2) + O(n) n>1
T(n) = O(nlogn)。

可证:快速排序在平均情况下的时间复杂度也是O(nlogn)。
快速排序算法的性能取决于划分的对称性。通过修改Partition ,我们完全可以设计出采用随机选择策略的快速排序算法。在快速排序算法的每一步中,当数组还没有被划分时,可以在a[p:r]中随机选出一个元素作为划分基准,这样可以使划分基准的选择是随机的,从而可以期望划分是较对称的。

template<class Type>
int RandomizedPartition (Type a[], int p, int r)
{
        int i = Random(p,r);//伪码,获取P与r之间的随机数。
        swap(a[i], a[p]);
        return Partition (a, p, r);
}

随机化算法对我很有启发。就这样吧。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

fuckguidao

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

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

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

打赏作者

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

抵扣说明:

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

余额充值