• 排序算法【2】——快速排序


    算法描述

    快速排序采用了分治的思想:

    • 分解:数组(A[pldots r])被划分为两个子数组(A[pldots q-1])(A[q+1ldots r]),使得(A[pldots q-1])中的元素小于等于(A[q])(A[q+1ldots r])中的元素大于等于(A[q])
    • 解决:通过递归调用快速排序,对子数组(A[pldots q-1])(A[q+1ldots r])进行排序

    伪代码

    在不同的快速排序算法实现中有不同的分区PARTITION策略

    QUICKSORT(A, p, r)
        if p < r
            q = PARTITION(A, p, r)
            QUICKSORT(A, p, q - 1)
            QUICKSORT(A, q + 1, r)
    
    PARTITION(A, p, r)
        i = RANDOM(p, r)
        exchange A[r] with A[i]
        x = A[r]
        i = p - 1
        for j = p to r - 1
            if A[j] <= x
                i = i + 1
                exchange A[i] with A[j]
        exchange A[i + 1] with A[r]
        return i + 1
    

    算法复杂度

    这里假设数组(A[pldots r])中元素互不相同

    • 最好情况:每次分区的时候,两边的分区大小一致,那么最多递归(log(n))层,每一层都会比对(n)次,所以时间复杂度为(Theta(nlog(n)))
    • 最坏情况:每次分区选的主元(A[q])都是当前数组中的最大值或者最小值,那么会递归(n)层,时间复杂度为(Theta(n^2))
    • 平均情况:令随机变量(X)表示整个快速排序过程中比对次数,那么(E(X)approx 2nlnn),所以时间复杂度为(Theta(nlog(n)))

    期望(E(X))的计算过程:
    令随机变量

    [X_{ij}= egin{cases} 1, & ext {if A[i] compared with A[j] during the process} \ 0, & ext{otherwise} end{cases} ]

    注意到,元素(A[i])(A[j])最多只会发生一次比较(比较之后主元不会进入分区),所以

    [E(X)=E(sum_{i=1}^{n-1}{sum_{j=i+1}^{n}{X_{ij}}}) ]

    同时(A[i])(A[j])发生比较的概率,就是元素(A[i])或者(A[j])作为区间(A[ildots j])中第一个被选取的主元,这一概率

    [Pr{A[i]\,or\,A[j]\,as\,the\,first\,pivot\,chosen\,from\,A[ildots j]}=frac{2}{j-i+1} ]

    所以

    [egin{align} E(X) &= E(sum_{i=1}^{n-1}{sum_{j=i+1}^{n}{X_{ij}}})\ &= sum_{i=1}^{n-1}{sum_{j=i+1}^{n}{E(X_{ij})}}\ &= sum_{i=1}^{n-1}{sum_{j=i+1}^{n}{frac{2}{j-i+1}}}\ &= sum_{i=1}^{n-1}{2(ln(n-i+2)-1)}\ &= sum_{i=1}^{n-1}{2(ln(i+2)-1)}\ &approx 2ln(n!)\ &approx 2nlnn\ end{align}]

    Hoare分区

    在上面朴素的分区策略中,无法将与主元相等的元素平均地划分到左右子区间,Hoare分区策略解决了这个问题,那就是从两边开始扫描:

    HOARE-PARTITION(A, p, r)
        i = RANDOM(p, r)
        pivot = A[i]
        p = p - 1
        while true
            while A[++p] < pivot
            while pivot < A[--r]
            if p < r
                exchange A[p] with A[r]
            else
                return p
    

    实际上,std::sort采用的也是这种方式,所以C++标准规定了传入的比较函数必须保证(cmp(a, b) != cmp(b, a)),比如:

    int n = 10000000;
    std::vector<int> vec(n);
    std::sort(vec.begin(), vec.end(), [] (int x, int y) {
        return x <= y;
    });
    

    上面的代码会造成内存越界,从而segmentfault

  • 相关阅读:
    今日头条 算法 架构
    什么才是真正的成长
    罗素 哲学 数学
    商业模式 广告 DSP
    人工智能 商业 落地 榜单
    【转】没有过时的CRM 图解大全
    20个人的初创公司,采用哪些技术栈和软件便于快速研发?
    【转】DevSecOps:打造安全合规的 DevOps 平台
    spring security HttpSessionEventPublisher & spring session HttpSessionListener
    JEECG codegenerate-3.6.3 maven
  • 原文地址:https://www.cnblogs.com/HachikoT/p/13942596.html
Copyright © 2020-2023  润新知