两个长度相同有序序列的中位数
我当时想的第一个简略算法:把两个序列合并后打印第n个元素,不出所料超时了
第二个思路,存储两个序列,然后轮流从两个序列里查找当前最小元素,找到第n个最小元素打印即可,这是个有序序列所以很好找
然后就是书上的二分法思路:
分别取l1,l2的中位数a,b,则并集序列的中位数在a,b之间。
这是显然的,假设a<b,若中位数小于a,在l1中有一半的元素大于a,在l2中有多于一半的元素大于a,则有多于一般的数大于"中位数",矛盾。
此时去掉l1左边的k个数,再去掉l2右边的k个数,中位数的位置不变,这玩意也好理解,在合并序列的两侧分别去除相同数目的元素,中位数位置不变
然后两个序列就这样不断二分求中位数,直到两个中位数相同,此即并集中位数,或两个取到边界,则较小的是中位数
当两个序列长度不相同时,我脑子不好用没想出来,在这里找到了能看懂的办法,而且用在长度相同序列好像还更简单
(解法4)
我自己的理解如下
设数组为长度为m的arr1和长度为n的arr2,将arr1分为左右两部分,左边有i个元素,右边有m-i个元素,同理将arr2分为j,n-j的两部分
显然0<=i<=m,0<=j<=n;
若n+m为偶数,当左边元素数i+j等于右边元素数n+m-i-j,且max(arr1[i-1],arr2[j-1])<min(arr2[i],arr2[j])时,两个和序列的中位数就为左边最大数和右边最小数的平均值
若n+m为奇数,不妨设中位数找到时左边数比右边数多一个,则当i+j=n+m+1-i-j,且max(arr1[i-1],arr2[j-1])<min(arr2[i],arr2[j])时,和序列的中位数为左边最大数
因为m,n都是整数,所以(m+n+1)/2与(m+n)/2相同,则j=(m+n+1)/2-i,此时只需要找到i的合适位置即可,用二分法进行查找
因为0<=i<=m,为使j>0,就需要m>=n;
c代码如下
#include<stdio.h> int max(int a,int b){return a>b?a:b;} int min(int a,int b){return a<b?a:b;} int main(){ int n,m,i,j,imax,imin,maxleft=0,minright=0,z; int arr1[100000],arr2[100000]; scanf("%d %d",&m,&n); //我这只适用m>=n的输入数据,实际使用时还需要修改 for(z=0;z<=m-1;z++) scanf("%d",&arr1[z]); for(z=0;z<=n-1;z++) scanf("%d",&arr2[z]); imax=m,imin=0; while(imin<=imax){
//以下四行是对i进行二分查找 i=(imax+imin)/2; j=(m+n+1)/2-i; if(i>0&&j<n&&arr1[i-1]>arr2[j]) imax=i-1; else if(j>0&&i<m&&arr2[j-1]>arr1[i]) imin=i+1;
else{ if(i==0) maxleft=arr2[j-1]; //arr1中所有的数都大于中位数,左边没有数了 else if(j==0) maxleft=arr1[i-1]; //arr2中所有的数都大于中位数,左边没有数了 else maxleft=max(arr1[i-1],arr2[j-1]); if((m+n)%2==1){ //和为奇数,直接输出左边最大值就行 printf("%d",maxleft); return 0; }
if(i==m) minright=arr2[j]; //arr1中所有的数都小于中位数,右边没有数了 else if(j==n) minright=arr1[i]; //arr2中所有的数都小于中位数,右边没有数了 else minright=min(arr1[i],arr2[j]); printf("%d",(minright+maxleft)/2); return 0; } } }

浙公网安备 33010602011771号