二分查找:寻找中位数。c++

二分查找:寻找中位数

给定一个 N 个数的数组 c[i],并用这个数组生成一个新数组 ans[i]。新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(c[i] - c[j]),1 <= i < j <= N。试求出这个新数组的中位数,中位数即为排序之后 (len+1)/2 位置对应的数字,’/’ 为下取整。

多组输入,每次输入一个 N,表示有 N 个数,之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <= n <= 1e5, 输出新数组的中位数

sample input:
4
1 3 2 4
3
1 10 2

sample output:
1
8

思路:

  • 首先想到的解决方式肯定是暴力,先枚举出所有的新数组元素,然后给数组排序,再根据位置找到中位数的值。由于原来的数字有n,进行减法的绝对值之后就会有Cn2个数,暴力绝绝对对会超时
  • 根据中位数的性质我们来进行简化,中位数的特征是它前面的数字与后面数字的个数是相同的,所以第一种想法是计算出最终数组中中位数的位置(从零开始所以偶数/2,奇数/2+1),然后定义左边界、右边界和中值。不断进行二分的查找,如果查找范围在中值左边,那么right=middle-1,再对左边进行查找;查找范围在右边,那么left=middle+1,再对右边进行查找。根据自己的结束条件来判定最终的结果是输出right还是left;
#include <iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100011];
int n;
int ans;
//int ans[1000000];
int find(int mid)
{
 int count=0,temp=0;
 for(int i=0;i<n;i++)
 {
  while(temp<n&&a[temp]-a[i]<mid)//判断差值和mid的位置
  temp++;//遍历 
  count+=temp-i-1; 
 }
 if(count>=ans)//是否符合条件 
 return 1;
 else return 0;
}
int main()
{
 while(scanf("%d",&n)!=EOF)
 {
  for(int i=0;i<n;i++)
  //cin>>a[i];
  scanf("%d",&a[i]);
  int num=n*(n-1)/2;
  if(num%2==0) ans=num/2;
  else ans=num/2+1;
  sort(a,a+n);
  int l=0,m=0,r=a[n-1]-a[0];
  while(r-l>=0)
  {
   m=(r+l)/2;
   if(find(m)==1)
   r=m-1;
   else l=m+1;
   } 
   //cout<<r<<endl;
   printf("%d\n",r);
 }
 return 0;
}
  • 当然,对于二分插值的查找,可以使用upper_bound和lower_bound函数进行计算。两个函数都是在左闭右开的有序区间之内进行二分的查找。
  • 对于upper_bound来说返回被查序列中第一个大于查找值的指针,对于lower_bound是返回被查序列中第一个大于等于查找值的指针。
  • 计算出原来数组的中位数,通过原来数组的中位数mid,用来判断新数组中中位数的位置
  • 如果原数组中a[j]>a[i]+mid (j>i),那么就说明这个值减去a[i]形成的新数列中的值比mid还要大,计算所有mid大的值,如果这个数比新数列的一半小,说明mid取大了;反之取小了。
  • 根据上面的情况,改变left,right的位置,从而得出最终的middle值。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[1000011];
int ans;
int n;
long long num;
bool find(int mid)
{
 int count=0;
 for(int i=0;i<n;i++)
 count+=a+n-lower_bound(a+i+1,a+n,a[i]+mid);//返回第一个大于等于x[i+mid值得下标
 //count计数所有差值比mid大得数列
 return count<= num/2;//count比mid大的个数 
}
int main()
{
 while(scanf("%d",&n)!=EOF)
 {
  for(int i=0;i<n;i++)
  {
   scanf("%d",&a[i]);
   //cin>>a[i];
  }
  num=n*(n-1)/2;
  sort(a,a+n);
  int left=0,right=a[n-1];
  while(right-left>=0)
  {
   int mid=(left+right)/2;
   if(find(mid)) right=mid-1;
   else left=mid+1;
  }
  printf("%d\n",right);
  //cout<<right<<endl;
 }
 return 0;
}
  • 这道题的坑点在哪呢?可以看一下上面两份代码的相似点是什么都注释掉了cin和cout。没错,这道题如果使用cin/out就会TLE,一定要用scanf和printf。原因是cin,cout在进行输入输出的时候都会先将内容放入缓存,时间开销会大。要么不用,要么关掉同步
  • ios::sync_with_stdio(false);
    cout.tie(NULL);
    随手加上正能量!
发布了29 篇原创文章 · 获赞 1 · 访问量 949

猜你喜欢

转载自blog.csdn.net/qq_44654498/article/details/104882700