abstract
本文是对快速排序算法的深入分析,主要讲解了快速排序算法的基本原理,时间复杂度分析以及如何去改进的快排算法,本文提供快排的伪代码,以及用Golang实现的具体代码。
这里补充一下一般算法设计的思路,首先根据问题分析建模,设计最初版本的代码,分析代码的running time,然后找到问题所在,解决问题。这样的。
the idea of algorithm
快速排序算法是典型的使用分治法设计的算法,算法主要分为:
divide:key这是快排的关键,
conquer:recursive sort 2 part
combine:这部分不做什么,因为快排不像merge sort没有消耗而外的空间
实现快排的关键就是在线性的时间,完成对子数组的分割
common quick sorting
我们先开快排的伪代码
//其中分割函数
partition(A, p, q)
X <- A[p]
curr <- p
for j <- p+1 to q
do if A[j] <= X
then curr <- curr+1
exch A[curr] A[j]
exch A[p] A[curr]
return curr
//对于combine部分
quickSort(A, p, q)
if p < q then
mid <- partion(A, p, q)
quickSort(A, p, mid-1)
quickSort(A, mid+1, q)
使用GO实现的代码如图所示:
func partition(A []int, left, right int) int{
midNum := A[left]
startFLag := left
for i := left+1; i <=right; i++{
if A[i] <= midNum{
startFLag++
A[i], A[startFLag] = A[startFLag], A[i]
}
}
A[left], A[startFLag] = A[startFLag], A[left]
return startFLag
}
func QuickSort(A []int, left
- [ ] List item
, right int) {
if right > left{
mid := partition(A, left, right)
QuickSort(A, left, mid-1)
QuickSort(A, mid+1, right)
}
}
下面说一下时间复杂度
上述算法的运行时间是取决于输入数据的情况。
我们来考虑==最好的情况(卵用没有)==假设每次选择的数据都会置换到当前数据段的中心,那递归公式为
T
(
n
)
=
2
T
(
n
/
2
)
+
Θ
(
n
)
T(n)= 2T(n/2)+\Theta(n)
T(n)=2T(n/2)+Θ(n) 所以其running time为
Θ
(
n
l
g
n
)
\Theta(nlgn)
Θ(nlgn)
我们考虑最坏的情况,每次置换过后的位置都是
l
e
f
t
+
1
left+1
left+1,那递归公式就是
T
(
n
)
=
T
(
0
)
+
T
(
n
−
1
)
+
Θ
(
n
)
T(n)=T(0)+T(n-1)+\Theta(n)
T(n)=T(0)+T(n−1)+Θ(n)所以其running time为
Θ
(
n
2
)
\Theta(n^2)
Θ(n2)
对于一个普通的情况,一般会偏向于好的情况一点,但是这种程度的算法是达不到实际使用需求的。
rand quick sorting
考虑到上面的问题,我们需要改进快速排序算法,其中最容易想到的是:将挑选的元素随机话。
func randPartition(A []int, left, right int) int{
key := int(rand.Int63n(int64(right-left))) + left
A[left], A[key] = A[key], A[left]
midNum := A[left]
startFLag := left
for i := left+1; i <=right; i++{
if A[i] <= midNum{
startFLag++
A[i], A[startFLag] = A[startFLag], A[i]
}
}
A[left], A[startFLag] = A[startFLag], A[left]
return startFLag
}
其中随机话处理的代码如上所示。这样处理能够带来什么样的好处了。这样能保证算法的
r
u
n
n
i
n
g
t
i
m
e
<
=
a
n
l
g
n
running\ time <= anlgn
running time<=anlgn
下面是纯数学分析,如果不想了解太深,完全没有必要看
在证明上面的结论上,需要使用到一个定律
∑
k
=
2
n
−
1
k
l
g
k
<
=
1
2
n
2
l
g
n
−
1
8
n
2
\sum_{k=2}^{n-1}klgk<={\frac 12}n^2lgn-{\frac 18}n^2
∑k=2n−1klgk<=21n2lgn−81n2
首先定义了一个这样的概率分布函数
x
k
=
1
(
i
f
t
h
e
r
e
i
s
a
p
a
r
t
i
t
i
o
n
k
:
n
−
k
−
1
)
a
n
d
x
k
=
0
(
o
t
h
e
r
)
x_k=1(if\ there\ is\ a\ partition\ k:n-k-1)\ and\ x_k = 0(other)
xk=1(if there is a partition k:n−k−1) and xk=0(other)
对于T(n)这个函数,它的表现形式就可以写成
T
(
n
)
=
∑
k
=
2
n
−
1
x
k
{
T
(
k
)
+
T
(
n
−
k
−
1
)
+
Θ
(
n
)
}
T(n)=\sum_{k=2}^{n-1}{x_k\{T(k)+T(n-k-1)+\Theta(n)\}}
T(n)=∑k=2n−1xk{T(k)+T(n−k−1)+Θ(n)}式中可以不考虑k=0和1的情况。
根据独立分布的特性可以推导为
E
(
T
(
n
)
)
=
2
n
∑
k
=
2
n
−
1
{
T
(
k
)
+
Θ
(
n
)
}
E(T(n))={\frac 2n}\sum_{k=2}^{n-1}{\{T(k)+\Theta(n)\}}
E(T(n))=n2∑k=2n−1{T(k)+Θ(n)}
即
E
(
T
(
n
)
)
=
2
n
∑
k
=
2
n
−
1
T
(
k
)
+
Θ
(
n
)
E(T(n))={\frac 2n}\sum_{k=2}^{n-1}T(k)+\Theta(n)
E(T(n))=n2∑k=2n−1T(k)+Θ(n)
再用代换法加上上面给出的定理来证明:
假设
<
=
2
n
∑
k
=
2
n
−
1
a
k
l
g
k
+
Θ
(
n
)
<={\frac 2n}\sum_{k=2}^{n-1}aklgk+\Theta(n)
<=n2∑k=2n−1aklgk+Θ(n)
<
=
2
a
n
(
1
2
n
2
l
g
n
−
1
8
n
2
)
+
Θ
(
n
)
<={\frac {2a}n}({\frac 12}n^2lgn-{\frac 18}n^2)+\Theta(n)
<=n2a(21n2lgn−81n2)+Θ(n)
<
=
a
n
l
g
n
−
a
4
n
+
Θ
(
n
)
<=anlgn -{\frac a4}n+\Theta(n)
<=anlgn−4an+Θ(n)
so when
a
4
n
>
Θ
(
n
)
{\frac a4}n>\Theta(n)
4an>Θ(n), the next is proved
<
=
a
n
l
g
n
<=anlgn
<=anlgn