• 【算法导论】快速排序


    1. 快速排序的描述

    1.1基本描述

      快速排序是一种时间复杂度为 O(n^2) 的排序算法。虽然最坏情况时间复杂度很差,但他的平均性能却很好,它的期望时间复杂度是 O(nlgn) 而且 O(nlgn) 中隐含的常数因子很小大约是1.44左右。
      快速排序与归并排序一样,也是基于归并的思想以下是对其子数组 A[ p…r ] 进行快速排序三步分治的过程:

    分解:数组 A[ p…r ] 被划分为两个子数组 A[ p…q-1] 和 A[q+1…r],使得 A[ p…q-1]中的每一个元素都小于等于 A[ q ] ,而 A[ q ]也小于等于 A[ q+1…r]中的每一个元素。

    解决:通过递归调用快速排序,对子数组A[ p…q-1] 和 A[q+1…r]进行排序。

    合并:因为子数组都是原址排序的,所以并不需要合并操作:A[ p…r ] 已经有序。

    快速排序伪代码:

    QUICKSORT(A,p,r)
        if p < r
            q = PARTITION(A, p ,r)
            QUICKSORT(A, p ,q-1)
            QUICKSORT(A, q+1 ,r)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中,q = PARTITION(A, p ,r) 所执行的操作就是分解操作,并返回 q。

    1.2 PARTITOION函数


    PARTITION函数伪代码:

    PARTITION(A, p, r) 
        x = A[r] 
        i = p - 1 
        for j = p to r - 1 
            do if A[j] ≤ x
                then i = i + 1 
                exchange A[i] with A[j] 
    exchange A[i + 1] with A[r] 
    return i + 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    PARTITION函数思路简介:

      其中 0 - i 维护的是小于等于A[ r ] 的序列,A[ i+1 ] 即为比 A[ r ] 大的第一个数,j从开始节点遍历到倒数第二个节点,遇到比 A[ r ] 小的数便进行 A[ j ] 与 A[ i+1 ] 的交换,以此维护了 0 - i 序列中比 A[ r ]小的特性。j 遍历完成后,即实现了A[0…i] 小于等于 A[ r ] ,A[i+1…j] 大于 A[ r ]。

    PARTITION函数执行过程:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    1.3 快速排序C++完整代码
    # include 
    using namespace std;
    int PARTITION(int A[],int p,int r)
    {
        int x = A[r];
        int i = p - 1;
        for(int j = p; j <= r - 1; j++)
        {
            if(A[j] <= x)
            {
                i = i + 1;
                swap(A[i],A[j]);
            }
        }
        swap(A[i+1] , A[r]);
        return i+1;
    }
    void QUICKSORT(int A[],int p,int r)
    {
        if(p < r)
        {
            int q = PARTITION(A,p,r);
            QUICKSORT(A,p,q-1);
            QUICKSORT(A,q+1,r);
        }
    }
    int main()
    {
        int A[100010];
        int n;
        cin>>n;
        for(int i=0;i<n;i++)
        {
            cin>>A[i];
        }
        QUICKSORT(A,0,n-1);
        for(int i=0;i<n;i++)
        {
            cout<<A[i]<<" ";
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    2. 快速排序的性能

    2.1 最坏时间复杂度

    因为无法举出使快速排序达到最坏情况的例子所以我们通过两步证明其最坏时间复杂度为 O( n^2 )

    1. 举一个例子证明其时间复杂度为 O( n 2 n^2 n2)
      当对快速排序进行分解的过程中得到的结果为一部分为 n 个元素,另一部分为0个元素时,该例的运行时间递归式为:
      T ( n ) = T ( n − 1 ) + T ( 0 ) + θ ( n ) = T ( n − 1 ) + θ ( n ) T(n)=T(n1)+T(0)+θ(n)=T(n1)+θ(n)
      T(n)=T(n1)+T(0)+θ(n)=T(n1)+θ(n)
      T(n)=T(n1)+T(0)+θ(n)=T(n1)+θ(n)
      可以得到: T ( n ) = θ ( n 2 ) T(n)=\theta(n^2) T(n)=θ(n2)
      由此可知, Quicksort 的最坏运行时间为: Ω( n 2 n^2 n2)
    2. 证明其时间复杂度不超过 O( n 2 n^2 n2)
      设T(n)是对大小为 n 的输入进行快速排序的最坏情况时间,则有递归:
      T ( n ) = max ⁡ 0 ≤ q ≤ n − 1 ( T ( q ) + T ( n − q − 1 ) ) + C 1 n T(n)=\max_{0 \leq q\leq n-1}(T(q)+T(n-q-1))+C_1n T(n)=0qn1max(T(q)+T(nq1))+C1n我们猜测对于某个常数C使得T (n) ≤ C,将这个猜测代入上面的递推式,我们得到:
      T ( n ) ≤ max ⁡ 0 ≤ q ≤ n − 1 ( C q 2 + C ( n − q − 1 ) 2 ) + C 1 n = C ∗ max ⁡ 0 ≤ q ≤ n − 1 ( q 2 + ( n − 1 − q ) 2 ) + C 1 n T(n)max0qn1(Cq2+C(nq1)2)+C1n=Cmax0qn1(q2+(n1q)2)+C1n
      T(n)max0qn1(Cq2+C(nq1)2)+C1n=Cmax0qn1(q2+(n1q)2)+C1n
      T(n)0qn1max(Cq2+C(nq1)2)+C1n=C0qn1max(q2+(n1q)2)+C1n
      由于 q 2 + ( n − 1 − q ) 2 q^2+(n-1-q)^2 q2+(n1q)2 q q q 的二次函数,求导可得,在区间 [1… n ] 范围内该函数只可能在 q = 1 , q = n , q = n / 4 q = 1,q = n,q = n/4 q=1q=nq=n/4,等三个点处取极值,由此可知:
      ∑ 0 ≤ q ≤ n − 1 ( q 2 + ( n − 1 − q ) 2 ) ≤ n 2 \sum_{0 \leq q\leq n-1}(q^2+(n-1-q)^2) \leq n^2 0qn1(q2+(n1q)2)n2所以有:
      T ( n ) ≤ C ( n − 1 ) 2 + C 1 n = C ∗ n 2 − 2 C n + C 1 n + C T(n)\leq C(n-1)^2+C_1n=C*n^2-2Cn+C_1n+C T(n)C(n1)2+C1n=Cn22Cn+C1n+C当取 C > C1 时, T ( n ) < = C n 2 T (n)<=Cn^2 T(n)<=Cn2 对所有 n > = 1 n >=1 n>=1 成立,由此证得快速排序时间复杂度不超过 O( n 2 n^2 n2)。
    2.2 平均时间复杂度

      设 T ( n ) T (n) T(n) 为输入规模为 n 时 QUICKSORT 算法的平均运行时间, T k ( n ) T_k(n) Tk(n)为所选划分元序号为 k+1 时 QUICKSORT 算法的平均运行时间,则 T ( n ) T (n) T(n) 满足以下递归方程:
    T ( n ) = ∑ k = 0 n − 1 ( p ( k + 1 ) T k ( n ) ) T(n)=\sum_{k=0}^{n-1} (p(k+1)T_k(n)) T(n)=k=0n1(p(k+1)Tk(n))其中 p ( k + 1 ) p(k+1) p(k+1)为划分元素序号为 k + 1 k+1 k+1的概率,我们可以知道划分元素序号的概率是相同的故: p ( k + 1 ) = 1 n p(k+1)=\frac{1}{n} p(k+1)=n1,带入上式可得:
    T ( n ) = ∑ k = 0 n − 1 1 n T k ( n ) ) = 1 n ∑ k = 0 n − 1 ( T ( k ) + T ( n − k − 1 ) + c n ) T(n)=\sum_{k=0}^{n-1} \frac{1}{n}T_k(n))= \frac{1}{n}\sum_{k=0}^{n-1} (T(k)+T(n-k-1)+cn) T(n)=k=0n1n1Tk(n))=n1k=0n1(T(k)+T(nk1)+cn)继续化简可得:
    1 n ( ∑ k = 0 n − 1 T ( k ) + ∑ k = 0 n − 1 T ( n − k − 1 ) ) + c n = 2 n ∑ k = 0 n − 1 T ( k ) + c n \frac{1}{n}(\sum_{k=0}^{n-1} T(k)+\sum_{k=0}^{n-1}T(n-k-1))+cn=\frac{2}{n}\sum_{k=0}^{n-1}T(k)+cn n1(k=0n1T(k)+k=0n1T(nk1))+cn=n2k=0n1T(k)+cn解递归方程可得:
    n T ( n ) = 2 ∑ k = 0 n − 1 T ( k ) + c n 2 nT(n)=2\sum_{k=0}^{n-1}T(k)+cn^2 nT(n)=2k=0n1T(k)+cn2 ( n − 1 ) T ( n − 1 ) = 2 ∑ k = 0 n − 1 T ( k ) + c n 2 (n-1)T(n-1)=2\sum_{k=0}^{n-1}T(k)+cn^2 (n1)T(n1)=2k=0n1T(k)+cn2两式相减,可得:
    n T ( n ) − ( n − 1 ) T ( n − 1 ) = 2 T ( n − 1 ) + c ( 2 n − 1 ) nT(n)-(n-1)T(n-1)=2T(n-1)+c(2n-1) nT(n)(n1)T(n1)=2T(n1)+c(2n1) T ( n ) n + 1 < = T ( n − 1 ) n + 2 c n \frac{T(n)}{n+1}<=\frac{T(n-1)}{n}+\frac{2c}{n} n+1T(n)<=nT(n1)+n2c
    G ( n ) = T ( n ) ( n + 1 ) G(n) = \frac{T(n)}{(n+1)} G(n)=(n+1)T(n)则有:
    G ( n ) ≤ C ( n − 1 ) + 2 c n = G ( n − 2 ) + 2 c ( 1 n − 1 + 1 n ) = G ( n − 3 ) + 2 c ( 1 n − 2 + 1 n − 1 + 1 n ) = G ( n − k ) + 2 c ( 1 n − k + 1 + . . . + 1 n − 1 + 1 n ) G(n)C(n1)+2cn=G(n2)+2c(1n1+1n)=G(n3)+2c(1n2+1n1+1n)=G(nk)+2c(1nk+1+...+1n1+1n)

    G(n)C(n1)+2cn=G(n2)+2c(1n1+1n)=G(n3)+2c(1n2+1n1+1n)=G(nk)+2c(1nk+1+...+1n1+1n)
    G(n)C(n1)+n2c=G(n2)+2c(n11+n1)=G(n3)+2c(n21+n11+n1)=G(nk)+2c(nk+11+...+n11+n1)整理后得到: G ( 1 ) + 2 c ∑ k = 0 n − 2 1 n − k = 2 c ∑ k = 2 n 1 k < = 2 c ∗ H n < = 2 c l o g n G(1)+2c\sum_{k=0}^{n-2}\frac{1}{n-k}=2c\sum_{k=2}^{n}\frac{1}{k}<=2c*H_n<=2clogn G(1)+2ck=0n2nk1=2ck=2nk1<=2cHn<=2clogn
    所以,Quicksort 算法的平均时间复杂度为: T ( n ) = G ( n ) ( n + 1 ) = θ ( n l o g n ) T(n)=G(n)(n+1)=\theta(nlogn) T(n)=G(n)(n+1)=θ(nlogn)

  • 相关阅读:
    photoshop插件开发入门(java,android,ios)
    【毕业设计】远程智能浇花灌溉系统 - stm32 单片机 嵌入式 物联网
    【Java】多态中调用成员的特点
    【咖啡品牌分析】Google Maps数据采集咖啡市场数据分析区域分析热度分布分析数据抓取瑞幸星巴克
    【100个 Unity实用技能】| C#中 Add 和 AddRange 的区别 及 使用示例
    使用 StringUtils.split 的坑
    RNN&GNU&LSTM与PyTorch
    【uniapp小程序】uploadFile文件上传
    JVM解析之类加载机制
    计算机视觉40例案例介绍
  • 原文地址:https://blog.csdn.net/qq_64585761/article/details/133429643