笔记
我们所熟知的插入排序、归并排序、快速排序等排序算法,它们的排序过程都依赖于比较元素间的大小,我们称这些算法为比较排序。本节讨论比较排序算法的运行时间的下界。
给定一个输入序列
<
a
1
,
a
2
,
…
,
a
n
>
<a_1, a_2, …,a_n>
<a1,a2,…,an>。为简化分析,假定所有输入元素都是互异的。
比较排序可以抽象为一棵决策树。下图决策树展示了插入排序算法作用于包含3个元素的输入序列的情况。决策树是一棵完全二叉树。每个非叶结点都以
i
:
j
i : j
i:j标记,表示一对元素
a
i
a_i
ai和
a
j
a_j
aj的比较过程。插入排序从根结点开始,根据结点的比较结果,逐层向下走。在一个非叶结点,如果比较之后确定两个元素的大小关系为
a
i
≤
a
j
a_i ≤ a_j
ai≤aj,那么进入左子树;如果比较之后确定两个元素的大小关系为
a
i
>
a
j
a_i > a_j
ai>aj,那么进入右子树。当到达一个叶结点时,表示已经排好序。每一个叶结点都表示一个可能的排好序的序列。
对于一个有
n
n
n个元素的序列来说,可能的排好序的序列一共有
n
!
n!
n!种,对应的决策树就有
n
!
n!
n!个叶结点。一个排序过程的运行时间取决于它所包含的比较次数。对于一个确定的排序过程,比较次数等于从决策树的根结点到一个叶结点的路径长度。因此,一个排序算法的最坏情况比较次数等于其决策树的高度,即从根结点到最低层次叶结点的路径长度。
考虑一棵高度为
h
h
h、具有
l
l
l个叶结点的决策树,它对应一个有
n
n
n个输入元素的排序算法。因为输入数据的
n
!
n!
n!种可能的排列都是叶结点,所以有
n
!
≤
l
n! ≤ l
n!≤l。由于在一棵高度为
h
h
h的二叉树中,叶结点的数目最多为
2
h
2^h
2h个,因此有
n
!
≤
l
≤
2
h
n! ≤ l ≤ 2^h
n!≤l≤2h。对该式取对数,得到
h
≥
l
g
(
n
!
)
=
Ω
(
n
l
g
n
)
h ≥ {\rm lg}(n!) = Ω(n{\rm lg}n)
h≥lg(n!)=Ω(nlgn)。于是可以得到,在最坏情况下,任何比较排序算法都至少需要做
Ω
(
n
l
g
n
)
Ω(n{\rm lg}n)
Ω(nlgn)次比较。
练习
8.1-1 在一棵比较排序算法的决策树中,一个叶结点可能的最小深度是多少?
略
8.1-2 不用斯特林近似公式,给出
l
g
(
n
!
)
{\rm lg}(n!)
lg(n!)的渐近紧确界。利用A.2节中介绍的技术来累加和
∑
k
=
1
n
l
g
k
\sum_{k=1}^{n}{\rm lg}k
∑k=1nlgk。
解
l
g
k
{\rm lg}k
lgk为单调递增函数,根据A.2节结论,可以通过积分近似方法来累加和
∑
k
=
1
n
l
g
k
\sum_{k=1}^n{\rm lg}k
∑k=1nlgk的渐近紧确界。
∑
k
=
1
n
l
g
k
=
l
g
1
+
∑
k
=
2
n
l
g
k
=
∑
k
=
2
n
l
g
k
≥
∫
1
n
(
l
g
k
)
d
k
=
n
l
g
n
−
(
n
−
1
)
l
n
2
\sum_{k=1}^n{\rm lg}k={\rm lg}1+\sum_{k=2}^n{\rm lg}k=\sum_{k=2}^n{\rm lg}k≥∫_1^n({\rm lg}k)dk=n{\rm lg}n-\frac{(n-1)}{{\rm ln}2}
∑k=1nlgk=lg1+∑k=2nlgk=∑k=2nlgk≥∫1n(lgk)dk=nlgn−ln2(n−1)
由上式可以得到,
∑
k
=
1
n
l
g
k
=
Ω
(
n
l
g
n
)
\sum_{k=1}^n{\rm lg}k=Ω(n{\rm lg}n)
∑k=1nlgk=Ω(nlgn)成立。
8.1-3 证明:对
n
!
n!
n!种长度为
n
n
n的输入中的至少一半,不存在能达到线性运行时间的比较排序算法。如果只要求对
1
/
n
1/n
1/n的输入达到线性时间呢?
1
/
2
n
1/2^n
1/2n呢?
解
一棵高度为
h
h
h的决策树最多有
2
h
2^h
2h个叶结点。
对于一个含
n
n
n个元素的序列,如果可能的排列一共有
n
!
/
2
n!/2
n!/2种,那么其决策树的叶结点数目满足
2
h
≥
n
!
/
2
2^h ≥ n!/2
2h≥n!/2。可以得到
h
≥
l
g
(
n
!
)
−
1
=
Ω
(
n
l
g
n
)
h ≥ {\rm lg}(n!) − 1 = Ω(n{\rm lg}n)
h≥lg(n!)−1=Ω(nlgn)。因此对于至少
n
!
/
2
n!/2
n!/2种可能的排列,比较排序算法也不能达到线性运行时间。
如果可能的排列一共有
n
!
/
n
n!/n
n!/n种,,那么其决策树的叶结点数目满足
2
h
≥
n
!
/
n
2^h ≥ n!/n
2h≥n!/n。可以得到
h
≥
l
g
(
n
!
)
−
l
g
n
=
Ω
(
n
l
g
n
)
h ≥ {\rm lg}(n!) − {\rm lg}n = Ω(n{\rm lg}n)
h≥lg(n!)−lgn=Ω(nlgn)。这种情况下,比较排序算法也不能达到线性运行时间。
如果可能的排列一共有
n
!
/
2
n
n!/2^n
n!/2n种,,那么其决策树的叶结点数目满足
2
h
≥
n
!
/
2
n
2^h ≥ n!/2^n
2h≥n!/2n。可以得到
h
≥
l
g
(
n
!
)
−
n
=
Ω
(
n
l
g
n
)
h ≥ {\rm lg}(n!) − n = Ω(n{\rm lg}n)
h≥lg(n!)−n=Ω(nlgn)。这种情况下,比较排序算法也不能达到线性运行时间。
8.1-4 假设现有一个包含
n
n
n个元素的待排序序列。该序列由
n
/
k
n/k
n/k个子序列组成,每个子序列包含
k
k
k个元素。一个给定子序列中的每个元素都小于其后继子序列中的所有元素,且大于其前驱子序列中的每个元素。因此,对于这个长度为
n
n
n的序列的排序转化为对
n
/
k
n/k
n/k个子序列中的
k
k
k个元素的排序。试证明:这个排序问题中所需比较次数的下界是
Ω
(
n
l
g
k
)
Ω(n{\rm lg}k)
Ω(nlgk)。(提示:简单地将每个子序列的下界进行合并是不严谨的。)
解
正如提示所言,我们不能简单将每个子序列的下界进行合并。因为这种做法引入了一个前提,那就是我们采用的排序算法必须是独立地对每个子序列进行排序的,元素间的比较仅限于各子序列内部。而对于那些存在跨子序列比较的排序算法,这一做法并没有考虑进来。因此这一做法是不严谨的。
我们还是需要将整个序列当做一个整体来考察。每个子序列都有
k
!
k!
k!种可能的排列,一共有
n
/
k
n/k
n/k个子序列,那么整个序列一共有
(
k
!
)
n
/
k
(k!)^{n/k}
(k!)n/k种可能的排列。假定针对该序列的排序算法的决策树的高度为
h
h
h,那么满足
2
h
≥
(
k
!
)
n
/
k
2^h ≥(k!)^{n/k}
2h≥(k!)n/k,两边取对数可得到
h
≥
l
g
[
(
k
!
)
n
⁄
k
]
=
n
k
l
g
(
k
!
)
=
Ω
(
n
l
g
k
)
h≥{\rm lg}[(k!)^{n⁄k} ]=\frac{n}{k}{\rm lg}(k!)=Ω(n{\rm lg}k)
h≥lg[(k!)n⁄k]=knlg(k!)=Ω(nlgk)