快速排序
时间复杂度为 θ ( n 2 ) \theta(n^2) θ(n2)
其实通常是最实用的,平均性能很好,期望时间复杂度是 θ ( n l g n ) \theta(nlgn) θ(nlgn),而且其中隐含的常数因子非常小。
工作原理:
数组A[p…r]被划分为两个(可能为空)子数组A[p…q-1]和A[q+1…r],使得A[p…q-1]中的每一个元素都小于等于A[q],而A[q]也小于等于A[q+1…r]中的每个元素,其中,计算下标q也是划分过程的一部分。
然后就是分治法那一套
最开始调用的是 Q U I C K S O R T ( A , 1 , A . l e n g t h ) QUICKSORT(A,1,A.length) QUICKSORT(A,1,A.length)
数组的划分
实现对子数组 A [ p . . r ] A[p..r] A[p..r]的原址重排
P A R T I T I O N PARTITION PARTITION总是选择一个 x = A [ r ] x=A[r] x=A[r]作为主元,并围绕它来划分子数组 A [ p . . r ] A[p..r] A[p..r]。
这样每一轮迭代开始时,对于任意数组下标 k k k,有:
P A R T I T I O N PARTITION PARTITION在子数组 A [ p . . r ] A[p..r] A[p..r]上的时间复杂度是 θ ( n ) \theta(n) θ(n),其中n=r-p+1
性能
最坏情况划分:
当划分产生的两个子问题分别包含了n-1个元素和0个元素时,为最坏情况。每一次调用都是最坏情况。
划分操作的时间复杂度是
θ
(
n
)
\theta(n)
θ(n),由于对一个大小为0的数组进行递归调用会直接返回,
T
(
0
)
=
θ
(
1
)
T(0)=\theta(1)
T(0)=θ(1),递归式为
T
(
n
)
=
T
(
n
−
1
)
+
T
(
0
)
+
θ
(
n
)
=
T
(
n
−
1
)
+
θ
(
n
)
T(n)=T(n-1)+T(0)+\theta(n)=T(n-1)+\theta(n)
T(n)=T(n−1)+T(0)+θ(n)=T(n−1)+θ(n)
显然
T
(
n
)
=
θ
(
n
2
)
T(n)=\theta(n^2)
T(n)=θ(n2)
这样看来最坏的情况时运行时间并不好于插入排序,当输入数组 完全有序时,快速排序的时间复杂度仍然为
θ
(
n
2
)
\theta(n^2)
θ(n2),同样情况下,插入排序的时间复杂度为
O
(
n
)
O(n)
O(n)
最好情况
一个规模是
⌊
n
/
2
⌋
\lfloor n/2 \rfloor
⌊n/2⌋,而另一个子问题的规模为
⌈
n
/
2
⌉
−
1
\lceil n/2 \rceil -1
⌈n/2⌉−1,得到递归式
T
(
n
)
=
2
T
(
n
/
2
)
+
θ
(
n
)
T(n)=2T(n/2)+\theta(n)
T(n)=2T(n/2)+θ(n)
得到解为
T
(
n
)
=
θ
(
n
l
g
n
)
T(n)=\theta(nlgn)
T(n)=θ(nlgn)
平衡划分
平均情况下,其实相较于最坏情况,都是更接近于最好情况的
只要划分是常数比例的,运行时间都是 O ( n l g n ) O(nlgn) O(nlgn)
我们这么来分析平均情况,可以视作最好的情况和最坏的情况交替出现在树的各层上。
比如图里第一层划分的代价为n,两个子数组的大小为n-1和0,下一层大小为n-1的子数组按最好情况划分为(n-1)/2和(n-1)/2-1两个子数组。在这里,我们设大小为0的子数组得到边界条件代价为1。
在一个差的划分后面接好的划分,大小分别为,(n-1)/2-1和(n-1)/2。组合的划分代价为 θ ( n ) + θ ( n − 1 ) = θ ( n ) \theta(n)+\theta(n-1)=\theta(n) θ(n)+θ(n−1)=θ(n),交替出现时的平均情况,说白了只是 O O O符号中隐含的常数因子要略大一些。
随机化版本
前提是输入数据的所有排列等概率。–采用随机抽样实现。从数组中随机选择一个元素作为主元。
分析
利用代入法,我们可以证明快速排序的时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。假设
T
(
n
)
T(n)
T(n)是最坏情况下AQUICKSORT在输入规模为n的数据集合上所花费的时间,则有递归式:
T
(
n
)
=
max
0
≤
q
≤
n
−
1
(
T
(
q
)
+
T
(
n
−
q
−
1
)
+
θ
(
n
)
)
T(n)=\max \limits_{0\leq q \leq n-1}(T(q)+T(n-q-1)+\theta(n))
T(n)=0≤q≤n−1max(T(q)+T(n−q−1)+θ(n))
我们不妨假设
T
(
n
)
≤
c
n
2
T(n)\leq cn^2
T(n)≤cn2成立,带入得:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oskB4GTA-1627713049951)(C:\Users\23030\AppData\Roaming\Typora\typora-user-images\image-20210731141858984.png)]
因此我们可以选择一个足够大得常数c,使得c(2n-1)项能显著大于 θ ( n ) \theta(n) θ(n)项,因此 T ( n ) = O ( n 2 ) T(n)=O(n^2) T(n)=O(n2)。特例:当非平衡时,快速排序的运行时间为 Ω ( n 2 ) \Omega(n^2) Ω(n2)。因此,快速排序的运行时间是 θ ( n 2 ) \theta(n^2) θ(n2)。
期望运行时间
为了分析RANDOMIZED-QUICKSORT的期望运行时间是 O ( n l g n ) O(nlgn) O(nlgn)。我们首先要理解划分操作,并以此推导出划分的一个 O ( l g n ) O(lgn) O(lgn)的界。再加上之前得到的最好情况界 θ ( n l g n ) \theta(nlgn) θ(nlgn)。那么开始我们的分析,首先假设:待排元素都是互异的。
运行时间和比较操作
Q U I C K S O R T QUICKSORT QUICKSORT和 R A N D O M I Z E D − Q U I C K S O R T RANDOMIZED-QUICKSORT RANDOMIZED−QUICKSORT除了如何选择主元元素有差异以外,其他地方完全相同。前者的主要时间花费在 P A R T I T I O N PARTITION PARTITION中,调用一次 P A R T I T I O N PARTITION PARTITION的时间为 O ( 1 ) O(1) O(1)再加上一段循环时间。而for循环的每一轮迭代都要在第4行进行一次比较,因此比较第4行所被执行的总次数,就能够得到for循环花费时间的界了。
引理:
当在一个包含n个元素的数组上运行 Q U I C K S O R T QUICKSORT QUICKSORT时,会选择一个主元元素,而最多会调用n次。假设在 P A R T I T I O N PARTITION PARTITION的第四行中所作比较的次数为 X X X,那么 Q U I C K S O R T QUICKSORT QUICKSORT的运行时间为 O ( n + X ) O(n+X) O(n+X)。
我们的目标将有且只有一个,那就是计算出X,即求出总的比较次数。将数组A的各个元素重新命名为 z 1 , z 2 , . . . , z n z_1,z_2,...,z_n z1,z2,...,zn,其中 z i z_i zi是数组A中第i小的元素,这样的话说白了 Z i j = z i , z i + 1 , . . . , z j Z_{ij}={z_i,z_{i+1},...,z_j} Zij=zi,zi+1,...,zj为 z i z_i zi与 z j z_j zj之间(含i和j元素)的元素集合。
我们定义:
注意,任意两个数之间会且仅会比较一次,我们有总的比较次数:
对上式两边同时取期望我们有:
z i j z_{ij} zij的出现,意味着i或j是主元
而上述两个时间其实是互斥的,因此有:
即:
综上,快速排序所用的期望时间是 O ( n l g n ) O(nlgn) O(nlgn).
###################################
最近看的进度有些拿不准。3天一节?貌似太快了