开头废话
这个问题是Donald.E.Knuth在他发表的论文Mathematical Analysis of Algorithms中提到的,这里对他的算法分析过程给出了更详细的解释。
问题描述:
给定一个数组a[1,2,...,n],用尽量少的比较次数找出数组中第t大的数。(假定这n个数两两不同)。
算法描述:
对于这个问题,可以很容易想到对应的算法。一个 \(O(n\log n)\) 的排序算法总能解决问题(然鹅今天我们并不对数组进行完全的排序)。
参照快速排序中的Partition操作,将元素a[i]放到某个位置\(k\),使得排在它前面的元素都比它大(但不一定按照从大到小的次序排列),后面的元素都比它小。再根据a[i]的位置\(k\)与\(t\)的大小关系,缩小查找范围再对子问题求解。
对于每一次Partition操作,会有这样的3种情况:
(1).若\(k=t\),算法结束。
(2).若\(k>t\),则对a[i]~a[k-1]递归地求解
(3).若\(k
时间复杂度分析
在这个问题的求解过程中,产生子问题的规模不断缩小。其中影响子问题的变量有\(n\)(数组的长度)和\(t\)(待查找的t)。Knuth记\(C_{n,t}\)为在\(n\)个元素的数组中选择第\(t\)大的数所需的平均比较次数,这里有一个前提,我们假设数组的排列是随机的,每一次Partition找到第1,第2,...,第n大的数概率均为\(\frac 1 n\)。
于是我们可以得到这样的式子:
\begin {aligned}
A_{n,t}&=C_{n-1,t-1}+C_{n-2,t-2}+\cdots+C_{n-t+1,1}\
B_{n,t}&=C_{t,t}+C_{t+1,t}+\cdots+C_{n-1,t}
\end {aligned}
\begin {aligned}
A_{n+1,t+1}&=C_{n-1,t-1}+C_{n-2,t-2}+\cdots+C_{n-t+1,1}+C_{n,t}=A_{n,t}+C_{n,t}\
B_{n+1,t}&=C_{t,t}+C_{t+1,t}+\cdots+C_{n-1,t}+C_{n+1-1,t}=B_{n,t}+C_{n,t}
\end{aligned}
(n+1)C_{n+1,t+1}-nC_{n,t+1}-nC_{n,t}+(n-1)C_{n-1,t}\
=(n+1)n-n(n-1)-n(n-1)+(n-1)(n-2)\+(A_{n+1,t+1}-A_{n,t})-(A_{n,t+1}-A_{n-1,t})+(B_{n+1,t+1}-B_{n,t+1})-(B_{n,t}-B_{n-1,t})
\
=2+C_{n,t}-C_{n-1,t}+C_{n,t+1}-C_{n-1,t}
(n+1)C_{n+1,t+1}-(n+1)C_{n,t+1}-(n+1)C_{n,t}+(n+1)C_{n-1,t}=2\\Downarrow\
C_{n+1,t+1}-C_{n,t+1}-C_{n,t}+C_{n-1,t}=\frac{2}{n+1}
\left{
\begin{array}{l}
C_{n,1}= n-1+\frac{1}{n}(C_{1,1}+C_{2,1}+\cdots +C_{n-1,1})\
B_{n,1}=C_{1,1}+C_{2,1}+\cdots+C_{n-1,1}\
B_{n+1,1}=B_{n,1}+C_{n,1}\
C_{n,1}=n-1+\frac{1}{n}(B_{n,1})\
C_{n+1,1}=n+\frac{1}{n+1}(B_{n+1,1})
\end{array}
\right.
\begin{aligned}
(n+1)C_{n+1,1}-nC_{n,1} &= (n+1)n-n(n-1)+C_{n,1}\
C_{n+1,1}-C_{n,1}&=2-\frac{2}{n+1} \quad\quad(*)
\end{aligned}
\left{
\begin{array}{c}
\begin{aligned}
C_{1,1}&=0\
C_{2,1}-C_{1,1}&=2-\frac22\
C_{3,1}-C_{2,1}&=2-\frac23\
\cdots\
C_{n,1}-C_{n-1,1}&=2-\frac2n\
\end{aligned}
\end{array}
\right.\
\begin{aligned}
C_{n,1}&=2(n-1)-2\sum_{k=2}^n \frac1k\
\quad\Downarrow
\
C_{n,1}&=2n-2\sum_{k=1}^n\frac1k=2n-2H_n
\end{aligned}
\left{
\begin{array}{l}
(C_{n+1,t+1}-C_{n,t})-(C_{n,t+1}-C_{n-1,t})=\frac2{n+1}\
(C_{n,t+1}-C_{n-1,t})-(C_{n-1,t+1}-C_{n-2,t})=\frac2{n}\
\quad\quad\quad\quad\quad\quad\quad\quad\cdots\
(C_{t+2,t+1}-C_{t+1,t})-(C_{t+1,t+1}-C_{t,t})=\frac2{t+2}\
\end{array}
\right.\
\begin{aligned}
C_{n+1,t+1}-C_{n,t}&=\frac{2}{n+1}+\frac{2}{n}+\cdots+\frac{2}{t+2}+C_{t+1,t+1}-C_{t,t}\
&=2(H_{n+1}-H_{t+1})+2-\frac{2}{t+1}
\end{aligned}
C_{n,t}=2\sum_{2\leq k\leq t}(H_{n-t+k}-H_{k}+1-\frac1k)+C_{n+1-t,1}
C_{n,t}=2((n+1)H_n-(n+3-t)H_{n+1-t}-(t+2)H_t+n+3),\quad(1\leq t\leq n)
C_{n,t}=O(n\log n)