kth element

第K小算法

问题

给定长度为nn的序列aa和整数k[1,n]k\in[1,n],求出aa中第kk小的数。

解法一:半个快排

使用快排思路,每次随机找一个数当作pivot,小于pivot放左边,大于pivot放右边,然后根据kk的大小决定去左边还是右边查找。复杂度期望O(n)O(n),最坏O(n2)O(n^2)

代码

解法二:median of medians

还是使用快排思路,这次用一个骚操作:

假设当前在找[l,r][l,r]内的第kk小,即solve(l, r, k)。尝试找当前序列的pivot时,先将所有数55个一组分组,得到n/5n/5个长度为55的子序列,对于每个子序列,找出中位数(这是O(1)O(1)的),得到一个长度为n/5n/5的中位数序列,再递归调用solve(1, n/5, n/10)找到这个中位数序列的中位数,作为pivot。

这个pivot一定不小于n/10n/10个中位数,也就是说不小于310n\frac{3}{10}n的数,同理也不大于310n\frac{3}{10} n的数。

写出递归表达式:

T(n)=T(710n)+T(15n)+O(n)T(n) = T(\frac{7}{10} n) + T(\frac{1}{5} n) + O(n)

可以证明T(n)=O(n)T(n) = O(n),证明如下:

  • n=1n=1时,成立;

  • 假设nk1n\leq k-1时满足T(n)BnT(n) \leq B\cdot n,则

    T(k)=T(0.7k)+T(0.2k)+Ck=0.7Bk+0.2Bk+Ck=(0.9B+C)k\begin{aligned}T(k) &= T(0.7 k) + T(0.2 k) + Ck\\ &= 0.7B k + 0.2 B k + Ck\\ &= (0.9B + C) k \end{aligned}

    只要0.9B+CB0.9B + C \leq B就可以,也就是说B10CB\geq 10C就行。取任意满足条件的BB即可得出T(n)=O(n)T(n)=O(n)

不过观察到B10CB\sim 10C,也就是说算法有个1010的常数,可能常数会比较大。随机情况下,差不多慢三倍。

代码