快速选择算法(基于快排)

快排给了我们一种快速选择数组第 k 小的思路:
        利用一个数组中的主元,消耗 O(n) 的时间将数组分为两部分(利用Qsort的Part函数):前半部分的值都小于等于主元,后半部分的值都大于主元,这样若前半部分元素的个数大于等于 k ,那么第k小的元素一定在前半部分,对前半部分递归调用即可;反之,则在后半部分,设前半部分有 t 个元素,那么第k小一定是后半部分中第 kt 小,递归调用即可,直到我们的区间中只有一个元素,结束递归,这玩意就是我们所需要的第 k 小.
这样我们消耗的时间为T(n)=T(k)+cn,其中, k 为我们所进入的那半部分元素的个数,当然,由于众所周知的快排不稳定性,即使是快速选择也有可能给我们玩出O(n2)的复杂度出来,我们的算法是很容易被卡的,如果加入随机化,即随机化地选取主元,我们就有令人安心的复杂度: O(n) ,设对 k<n 都有 E[T(k)]pk ,那么对 k=n ,我们有

E[T(n)]=E[T(k)]+cni=1n1E[T(max{ni,i})]n1+cn

E[T(n)]p3n24(n1)+cn<p3n2+n44(n1)+cnpnp>4cnn4
当然, n 时, p4c 因此,我们的时间复杂度可以为 T(n)=4cn 是线性的.
但我们总是不满意,一个运行时间期望为 O(n) 的算法可以运行 O(n2) 之久,我们想要一个最坏 O(n) 的算法,这是有的:
我们考虑使快速选择算法运行时间达到 O(n2) 的原因:主元的选择.
这样,我们考虑主动为Part函数选择一个合适的主元:它应该稳定在 (nk,nt) 之间,这样我们就避免了极端情况的发生,从而使我们的快速选择能稳定下来,
我们考虑这样的启发式策略:将数组分为 nc 个相等的部分,每个部分均有 c 个元素(最后一个部分可能有不到c个元素,但我们同样可以处理,同时也不影响复杂度,我这里为节约字数(挺累的),就不再考虑了).我们将 nc 个小部分简单排序,取每个小部分的中位数,再在所取的中位数中找到他们的中位数,这个中位数 x 就是我们合适的主元.
我们来想一想,在我们得到的nc个中位数中, x 的前面有n2c个数,而这 n2c 个数是原来几个小部分的中位数,每个中位数的前面都有 c2 个数,故而我们所得到的 x 最小为第c2n2c个数,类似地, x 的后面最少也有c2n2c个数(这里并不精确地给出值,而只是相差几个小常数,这无关紧要,我们只要 n 的系数).
这样我们就稳定地给出了一个合适的主元x,它可以稳定地将数组划分为两个差别不是特别大的部分,通过对合适的部分进行递归调用,我们就可以快速求出第 k 小;
下面,我们来求合适的c,并证明选取合适的 c 可以使我们在O(n)时间内完成计算:
首先,我们总是碰到了最坏情况:
T(n)=T(nc)+T(nc2n2c)+q.c2.nc

首先:我们要保证
nc+nc2n2c<n

否则我们就会得到 O(nlogn) 的时间复杂度,这不是我们所期望的;
解上述不等式,解得 c>4
k<n 我们有 T(k)pk 成立,那么对 n 我们有:
T(n)pnc+p(nc2n2c)+qcnpn

我们得到
pqc2c22

最后,我们给 p 取最优值得到c=5:p50q;c=7:p49q
我们取 c=5,c=7 都是合适的,都可以使我们的快速选择算法稳定于 O(n) ,
从而,我们得到了稳定的快速选择算法;(好累啊,休息一下).

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值