随机选择算法

问题

如何从一个无序数组中求出第 K 大的数(为了简化讨论,假设数组中的数各不相同)。

例如,对数组 { 5,12,7,2,9,3 }来说,第三大的是 5,第五大的是 9

随机选择算法

概念

最直接的想法是对数组排序,然后取出第 K 个。这样做法时间复杂度 O(nlogn),虽然看起来很好,但是还有更优的算法。

随机选择算法,对任何输入都可以达到 O(n)的期望时间复杂度

随机选择算法的原理类似于 随机-快速排序

假设主元为 A[p],当对 A[left,right] 执行一次 randPartition 函数之后,A[p] 左侧的元素个数就是确定的(p - left 个),而且他们都小于主元,那么 A[p] 就是第 p-left+1 大的数

不妨令 M = p - left + 1

  • 如果 K==M,说明第 K 大的就是主元

  • 如果 K < M,说明第 K 大的在主元的左边,也就是 [left,p-1] 的第 K 大,应该向左侧元素递归

  • 如果 K > M,说明第 K 大的元素在右边,也就是 [p+1,right] 的第 K-M 大,应该向右侧元素递归

    右侧的第 K-M 大

算法以 left==right 为递归边界,返回 A[left]

代码实现

// 随机选择算法,从 [left, right] 中返回第 K 大的数
public static int randSelect(int[] A, int left, int right, int K){
   
    if(left == right) {
   
        return A[left];	//边界
    }
    int p = randPartition(A, left, right);	// 划分后主元的位置 P
    int M = p - left + 1;	// A[p] 是[left, right]中的第M大
    if(K == M) {
   	//找到第K大的数
        return A[p];
    } else if(K < M){
   	// 第 K 大元素在主元左侧
        return randSelect(A, left, p-1, K);	// 向主元左侧找第 K 大
    } else {
   	// 第 K 大的元素在主元右侧
        return randSelect(A, p+1, right, K-M);	// 向主元右侧找第 K 大
    }
}

复杂度

可以证明,虽然最坏时间复杂度为 O( n 2 n^2 n2),但是其对任意输入的期望时间复杂度确是 O(n)

这意味着不存在一组特定的数据能使这个算法出现最坏情况,是个相当使用和出色的算法(详细证明可以参考算法导论)

应用

问题

给一个由各不相同的整数组成的集合,现在要将它分成两个子集,使:两子集合的并为原集合、两子集合的交为空集,同时在两个子集合的元素个数 n 1 n_1 n1

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值