一开始看这道题目的时候让我想起来以前看过的一道面试题,结果写上去超时。。
接下来入正题,那道面试题我在最后分享一下。
数据只有8000,n^2能过,只能暴力枚举每个数了。
首先把大于arr[i]的数改成1小于改成-1,等于改成0(其实只有自己等于自己)
一般连续区间的题目大概率都用到线段树和前缀和,这里用到了前缀和。
从i开始到n把前缀和都记录下来,这样如果前缀和为0就表示该数为中位数。(右扫描)
接下来在计算i到1的(左扫描),计算过程中如果左边的前缀于右边的前缀和相加为0,
那么要ans++,但这样比较费时,那么用Hash数组记录下右边前缀和,下标表示前缀和,值表示该等于前缀和的前缀有多少个,
这样ans+=Hash[],就大大节约了时间,但是前缀和可能为负数,所以加一个偏移量med=8000;
代码如下:
#include<iostream>
#include<cstring>
using namespace std;
int arr[8005];//数组
int sym[8005];//0 -1 1 的标志
int sum[8005];//前缀和
int Hash[8005*2];//以前缀和为下标记录等于前缀和的前缀个数
const int med=8000;
int main()
{
int n;
while(cin>>n)
{
for(int i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
}
for(int i=1;i<=n;i++)
{
memset(Hash,0,sizeof(Hash));
for(int j=1;j<=n;j++)
{
if(arr[i]>arr[j])
sym[j]=-1;
else if(arr[i]<arr[j])
sym[j]=1;
else sym[j]=0;
}
sum[i]=0;
Hash[med+sum[i]]++;
for(int j=i+1;j<=n;j++)//右扫描
{
sum[j]=sum[j-1]+sym[j];
Hash[med+sum[j]]++;
}
int ans=0;
ans+=Hash[med+0];
for(int j=i-1;j>=1;j--)//左扫描更新答案
{
sum[j]=sum[j+1]+sym[j];
ans+=Hash[med-sum[j]];
}
if(i!=n) printf("%d ",ans);
else printf("%d\n",ans);
}
}
}
分享一下我见的那道面试题:
给你一个随时增加元素的数组,要求更新他的中位数。
当第一个元素来的时候中位数即为该数,
这个时候维护一个大顶堆和一个下顶堆
大顶堆放入比中位数小的元素,小顶堆放入比中位数大的元素。
当左右堆的大小差值超过1时,便调整左右堆,中位数加入元素更少的一个堆
然后中位数等于另一个堆的堆顶元素,这样便实现了动态调整。
维护的时间复杂度是 lon n。