分治策略 - 典型实例 - 快速排序算法

快速排序算法

基本思想:被排序数组为A,用数组首元素作为标准将A分成前后两部分,比首元素小的元素构成数组的前部分,比首元素大的部分构成数组的后部分。这两部分构成两个新的子问题,算法接着对这两个数组进行递归。

Quicksort(A,p,r) //p,r分别为数组A的首元素和尾元素的下标

输入:数组A[p..r],1<=p<=r<=n
输出:从A[p]到A[r]按递增顺序排好序的数组A

if p<r
then q <- Partition(A,p,r)   //划分数组,找到首元素A[p]在排好序后的位置q
     A[p] <-> A[q]
     Quicksort(A,p,q-1)
     Quicksort(A,q+1,r)

Partition是划分的过程

x <- A[p]
i <- p+1
j <- r+1
while true do
    repeat j <- j-1
    until A[j] <= x
    repeat i <- i+1
    until A[i] > x
    if i < j
    then A[i] <-> A[j]
    else return j

下面举一个例子
image

算法的时间复杂度分析

  1. 每个元素都要和首元素进行一次比较(在i,j相遇位置附近的元素可能比较2次),所以划分过程的工作量是O(n)。
  2. 两个子问题递归调用的工作量

快速排序算法的各种情况分析

1.

均等划分子问题

先看均等划分的例子,如果每次划分得到的子问题大小都相等,即每个子问题的规模都等于n/2,那么在当前实例下时间复杂度函数的递推方程是:

\[\left\{ \begin{array}{lr} T(n)=2T(n/2)+O(n) & \\ T(1)=0& \end{array} \right. \]

根据主定理,该方程的解\(T(n)=O(nlogn)\),这是一种比较好的情况。

2.

子问题规模不同,但遵从一定比例

即使子问题规模不一样,但两个子问题的规模遵从一定的比例,比如1:9,那么时间复杂度函数的递推方程为:

\[\left\{ \begin{array}{lr} T(n)=T(n/10)+T(9n/10)+O(n) & \\ T(1)=0& \end{array} \right. \]

image
c是常数

这棵树不均衡,从树根到最左边树叶的路径最短,在这条路径上,每层的子节点的值是父节点值的1/10,设树根是第0层,每层为第k层,
\(\frac{1}{10}\)kn代表当前第k层的值,即子问题规模。
\(\frac{1}{10}\)0
n=n,代表第0层值为n,
\(\frac{1}{10}\)k*n=1,即子问题规模为1,到达树叶时,k=\(\log\)10(\(x\))。
同理得到最右边路径长度是log10/9n,为了表示时间渐进的上界,不妨取最长路径作为树的层次,即所有节点的数值之和为

T(n) = c\(n\log\)10/9\(n\) = O(\(n\log(n)\))

这说明,即使子问题规模不均衡,但是只要比例一定,快速排序算法的时间复杂度仍旧是\(O(n\log(n))\)

3.

下面介绍极端不均衡的情况

即划分后两个子问题的规模一个是0,另一个是\(n-1\)的情况,当数组元素是按从小到大正排序或从大到小逆排列时,就会呈现这种划分。

\[\left\{ \begin{array}{lr} W(n)=W(n-1)+O(n) & \\ W(1)=0& \end{array} \right. \]

根据迭代归纳,此时\(W(n)=O(n^2)\)
但最坏情况出现的概率很低,它的平均性能还是不错的。

4.

平均情况

假设交换后,数组A的首元素在排好序后处在\(n\)个位置中的任何位置都是等可能的,即它处在任何位置的概率都是\(1/n\)。如果它处在位置\(i\)\(i\)=1,2,...,\(n\)),那么划分后的两个子问题规模分别是\(i-1\)\(n-1\)。考虑到\(T(0)=0\),因此可以得到
image
当把\(O(n)\)看作\(n-1\)时,这个方程的解是\(T(n)=O(nlogn)\)
对于排序问题,平均情况下效率最高的算法就是时间复杂度为\(O(nlogn)\)的算法。在这个情况下,快速排序算法是平均情况下效率最高的算法之一。

posted @ 2020-03-26 22:59  HIIM  阅读(666)  评论(0编辑  收藏  举报