Median(二分)寻找差值的中位数

给了N个数字, X 1 … X n X_1\dots X_n X1Xn对于每对数字的差值 ∣ X i − X j ∣ |X_i-X_j| XiXj,找出这些差值的中位数

思路

  • 用的是双重二分
  • 我们取最大的元素减去最小的元素作为二分答案开始查找,第一重二分查找,遍历这个数组
  • 对于数组的每个元素来说(数组已经排好序了),如果我们发现,这个二分答案大于这个最大值减去当前值 a [ n − 1 ] − a [ i ] a[n-1]-a[i] a[n1]a[i],那么说明当前的二分答案小了,所以直接停掉这个遍历就行了(因为后面的比 a [ i ] a[i] a[i]大更不会符合这个条件)
  • 如果小于等于二分答案的数(最大的减这个都满足了二分答案了,那么不是最大的元素减去这个数肯定也小于二分答案了)我们就要进行第二重二分去统计这些都小于这个二分答案的对数
  • 这里再补充,关于这些元素的排列组合我们可以找到 C 2 n C^n_2 C2n种排序方法,我们把它除以二得到了绝对值点对的一半所以不管是偶数还是奇数(存在一个取整的问题)奇数的话就是下一个比这个大的数,偶数的话就是下一个比这个大的数和前面的平均,所以说他必须要大于这个位置才行
  • 对于第二重二分,我们统计比这个二分答案加上当前的元素,二者的和第一个大于等于(反正数组元素不会重复)他的元素的位置,那么以后的都是不合格的。
  • PS:如果最后统计的和刚好等于中位数的一半,那也不一定是中位数,我们必须继续缩小范围找到最小范围内符合条件的值。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int a[N] , sum = 0 , n , k;
int cal(int x){
	int l = 0 , r = n;
	while(l < r){
		int mid = (l + r)>>1;
		if(x < a[mid]){
			r = mid;
		}else{
			l = mid + 1;
		}
	}//查找第一个大于等于key的位置
	return n - r;
}
bool check(int x){
	for(int i = 0 ; i < n ; i++){
		if(a[i] + x > a[n - 1]) break;
		sum += cal(a[i] + x);
	}
	if(sum <= k) return true;
	return false;
}
int main(){

	while(scanf("%d",&n)!=EOF&&n){
		for(int i = 0 ; i < n ; i++){
			scanf("%d",&a[i]);
		}
		sort(a,a+n);
		k = (n - 1)*n/4;//排列次数的中位数
		int l = 0 , r = a[n - 1] - a[0];//以 0 和差的最大值二分答案
		while(l < r){
			int mid = (l + r) >> 1;
			if(check(mid)){
				r = mid;
			}else{
				l = mid + 1;	
			}
			sum = 0;
		}
		printf("%d\n",r);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百青青年旅馆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值