算法复习——分而治之篇之快速排序

算法复习——分而治之篇之快速排序

以下内容主要参考中国大学MOOC《算法设计与分析》,墙裂推荐希望入门算法的童鞋学习!

1. 从归并排序到快速排序

对归并排序不了解的同学,可阅读算法复习——分而治之篇之归并排序

​ 归并排序:简化分解,侧重合并

​ 快速排序:侧重分解,简化合并

2. 数组划分

快速排序的分解

2.1 基本思想

  • 任选元素 x x x作为分界线,称为主元(pivot)

在这里插入图片描述

  • 交换重排,满足 x x x左侧元素小于右侧

    在这里插入图片描述

2.2 实现方法

  • 选取固定主元 x x x(如尾元素)

  • 维护两个部分的右端点变量 i i i j j j

    在这里插入图片描述

  • 考察数组元素 A [ j ] A[j] A[j],只和主元比较

    • A [ j ] ≤ x A[j] \leq x A[j]x,则交换 A [ j ] A[j] A[j] A [ i + 1 ] A[i+1] A[i+1] i i i j j j右移
    • A [ j ] > x A[j] > x A[j]>x,则 j j j右移

2.3 伪代码

Partition(A, p, r)

输入:数组 A A A,起始位置 p p p,终止位置 r r r

输出:划分位置 q q q

x ← A[r]
i ← p - 1
for j ← p to r - 1 do
	if A[j] <= x then
		exchange A[i+1] with A[j]
		i ← i + 1
	end
end
exchange A[i+1] with A[r]
q ← i + 1
return q

3. 快速排序伪代码

初始调用:QuickSort(A, 1, N)

QuickSort(A, p, r)

输入:数组A,起始位置p,终止位置r

输出:有序数组A

if p < r then
	q ← Partition(A, p, r)
	QuickSort(A, p, q-1)
	QuickSort(A, q+1, r)
end

​ 在复杂度分析时,左右部分的子问题规模不确定,该如何分析时间复杂度呢?

4. 快速排序复杂度分析

4.1 最好情况

​ 数组划分后,每次主元都在中间,就是最好情况。时间复杂度 T ( n ) = 2 T ( n 2 ) + O ( n ) = O ( n l o g n ) T(n)=2T(\frac{n}{2})+O(n)=O(n log n) T(n)=2T(2n)+O(n)=O(nlogn)

4.2 最坏情况

​ 数组划分后,每次主元都在一侧,就是最坏情况。时间复杂度 T ( n ) = T ( n − 1 ) + T ( 0 ) + O ( n ) = O ( n 2 ) T(n)=T(n-1)+T(0)+O(n)=O(n^2) T(n)=T(n1)+T(0)+O(n)=O(n2)

5. 随机划分

5.1 反思最差情况

​ 数组划分时选取固定位置主元,可以针对性构造最差情况

5.2 解决方案

​ 数组划分时选取随机位置主元,无法针对性构造最差情况

5.3 伪代码

Randomized-Partition(A, p, r)

输入:数组A,起始位置p,终止位置r

输出:划分位置q

s ← Random(p, r)
exchange A[s] with A[r]
q ← Partition(A, p, r)
return q

6. 随机化快速排序伪代码

Randomized-QuickSort(A, p, r)

输入:数组A,起始位置p,终止位置r

输出:有序数组A

if p < r then
	q ← Randomized-Partition(A, p, r)
	Randomized-QuickSort(A, p, q-1)
	Randomized-QuickSort(A, q+1, r)
end

7. 随机化快速排序复杂度分析

7.1 分析目标:期望复杂度

​ 计算元素期望比较次数 E [ X ] E[X] E[X]

​ 基于比较的排序算法,实际上影响排序性能和运行时间的就是它的比较次数,因此期望运行时间,就是期望的比较次数。

7.2 符号表示

  • z k z_{k} zk:数组 A A A中第 k k k小的元素
  • 集合 S i , j S_{i, j} Si,j { z i , … , z j } \{z_{i}, \dots, z_{j}\} { zi,,zj}

7.3 推导过程

​ 如果我们在 S 1 , n S_{1,n} S1,n中选取了 z k z_{k} zk作为主元的话,就将数组一分为三,分成了 s 1 , k − 1 s_{1, k-1} s1,k1 z k z_{k} zk S k + 1 , n S_{k+1, n} Sk+1,n,如下图所示。

在这里插入图片描述

​ 我们可以定义随机变量 X i j X_{ij} Xij z i z_{i} zi z j z_{j} zj比较的次数,则 E [ X ] = E [ ∑ i = 1 n − 1 ∑ j = i + 1 n X i j ] = ∑ i = 1 n − 1 ∑ j = i + 1 n E [ X i j ] 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}] E[X]=E[i=1n1j=i+1nXij]=i=1n1j=i+1nE[Xij]。那么,关键就是计算 X i j X_{ij} Xij的数学期望。

​ 任何两个变量 z i z_{i} zi z j z_{j} zj在整个随机化快速排序的过程中,都可以分成三个时期: S i , j S_{i, j} Si,j划分前, S i , j S_{i,j} Si,j划分时, S i , j S_{i, j} Si,j划分后。这如何理解?

​ 假设 i < j i < j i<j。如果在当前这次随机划分时选取的主元 k < i k < i k<i k > j k > j k>j,那么经过这次随机划分后, z i z_{i} zi z j z_{j} zj依然在同一个数组中,这都属于 S i , j S_{i,j} Si,j划分前 的时期;直到某一个随机划分时选取的主元 i ≤ k ≤ j i \leq k \leq j ikj,那么经过这次随机划分后, S i , j S_{i,j} Si,j也一定为三, S i , k − 1 S_{i, k-1} Si,k1在一个数组中, S k + 1 , j S_{k+1, j} Sk+1,j在另一个数组中,那么 z i z_{i} zi z j z_{j} zj就被分开了,这属于 S i , j S_{i,j} Si,j划分时 的时期;之后,就都属于 S i , j S_{i,j} Si,j划分后 的时期了。

​ 由于在每一轮随机划分时,每个元素都只与主元比较,因为在 S i , j S_{i,j} Si,j划分前,选取的主元 z k z_{k} zk要么 k < i k < i k<i,要么 k > j k > j k>j,总之 z i z_{i} zi z j z_{j} zj一定不是主元,所以在 S i , j S_{i,j} Si,j划分前 z i z_{i} zi z j z_{j} zj 不可能发生比较

​ 在 S i , j S_{i,j} Si,j划分后 z i z_{i} zi z j z_{j} zj就属于不同的数组了,就再也不可能发生比较了。

​ 在 S i , j S_{i,j} Si,j划分时 z i z_{i} zi z j z_{j} zj就有可能发生比较了。还是那句话,在每一轮随机划分时,每个元素都只与主元比较,所以 z i z_{i} zi z j z_{j} zj发生比较的可能性 就是 z i z_{i} zi z j z_{j} zj被选为主元的可能性

​ 综上所述,
E [ X i j ] = P r { 在 S i , j 划 分 时 z i 或 z j 被 选 为 主 元 } = P r { z i 是 主 元 } + P r { z j 是 主 元 } = 1 j − i + 1 + 1 j − i + 1 = 2 j − i + 1 E[X_{ij}] =Pr\{在S_{i, j}划分时z_{i}或z_{j}被选为主元\}\\ =Pr\{z_{i}是主元\}+Pr\{z_{j}是主元\}\\ =\frac{1}{j-i+1}+\frac{1}{j-i+1}\\ =\frac{2}{j-i+1} E[Xij]=Pr{ Si,jzizj}=Pr{ zi}+Pr{ zj}=ji+11+ji+11=ji+12
​ 在得到 E [ X i j ] E[X_{ij}] E[Xij]后,就可以将其代入计算 E [ x ] E[x] E[x]
E [ X ] = ∑ i = 1 n − 1 ∑ j = i + 1 n 2 j − i + 1 = ∑ i = 1 n − 1 ∑ k = 1 n − i 2 k + 1 < ∑ i = 1 n − 1 ∑ k = 1 n 2 k = ∑ i = 1 n − 1 O ( l o g n ) = O ( n l o g n ) E[X] =\sum_{i=1}^{n-1} \sum_{j=i+1}^{n}\frac{2}{j-i+1}\\ =\sum_{i=1}^{n-1}\sum_{k=1}^{n-i}\frac{2}{k+1}\\ <\sum_{i=1}^{n-1}\sum_{k=1}^{n}\frac{2}{k}\\ =\sum_{i=1}^{n-1}O(logn)\\ =O(nlogn) E[X]=i=1n1j=i+1nji+12=i=1n1k=1nik+12<i=1n1k=1nk2=i=1n1O(logn)=O(nlogn)

​ 其中, k = j − i k=j-i k=ji,并且用到了调和级数 ∑ k = 1 n 1 k = O ( l o g n ) \sum_{k=1}^{n}\frac{1}{k}=O(logn) k=1nk1=O(logn)

​ 所以,随机化快速排序的期望时间复杂度是 O ( n l o g n ) O(n log n) O(nlogn)

8. 排序算法比较

算法名称 时间复杂度
选择排序 O ( n 2 ) O(n^2) O(n2)
插入排序 O ( n 2 ) O(n^2) O(n2)
归并排序 O ( n l o g n ) O(nlogn) O(nlogn)
快速排序 最差: O ( n 2 ) O(n^2) O(n2);最好: O ( n l o g n ) O(nlogn) O(nlogn);期望: O ( n l o g n ) O(nlogn) O(nlogn)

​ 基于比较的排序,时间复杂度下界为 Ω ( n l o g n ) \Omega(nlogn) Ω(nlogn)

猜你喜欢

转载自blog.csdn.net/NickHan_cs/article/details/111657282